Commit 688ab046 authored by 神楽坂玲奈's avatar 神楽坂玲奈

init

parent f071789f
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
{
"printWidth": 140,
"singleQuote": true
}
FROM node:stretch
FROM node
RUN apt-get update
RUN apt-get install -y kmod ipset gettext
RUN mkdir -p /usr/src/app
......
{
"server_address": "127.0.0.1",
"server_port": 9999,
"port": 9998,
"timeout": 10,
"interval": 1000
}
pg = require 'pg'
module.exports =
init: (server_id, database, callback)->
pg.connect database, (error, client, done) ->
throw error if error
client.query 'SELECT *, host(address) as host, network(address) as network FROM servers', (error, result) ->
throw error if error
servers = {}
for row in result.rows
row.private_address ?= row.public_address
row.tos = '0x' + row.tos.toString(16) if row.tos?
servers[row.id] = row
servers[server_id].next_hop = server_id
client.query 'SELECT * FROM links WHERE "from" = $1::smallint or "to" = $1::smallint', [server_id], (error, result) ->
throw error if error
for row in result.rows
# row.mode = row.mode.split('-')
# if row.mode.length > 1
# row.mode =
# mode: row.mode[0]
# encap: row.mode[1]
# sport: row.mode[2]
# dport: row.mode[3]
# else
# row.mode =
# mode: row.mode[0]
if row.from == server_id
servers[row.to].link = row.mode
servers[row.to].next_hop = row.to
else
servers[row.from].link = row.mode
servers[row.from].next_hop = row.from
client.query 'SELECT id FROM regions', (error, result) ->
throw error if error
regions = {}
for row in result.rows
regions[row.id] = id: row.id, addresses: []
client.query 'SELECT region_id, address FROM addresses', (error, result) ->
throw error if error
for row in result.rows
regions[row.region_id].addresses.push row.address
reachable_servers = (server.id for i, server of servers when server.link?)
reachable_servers.push server_id
client.query 'SELECT DISTINCT ON (region_id) region_id, server_id FROM gateways WHERE server_id = ANY($1::smallint[]) ORDER BY region_id, server_id = $2::smallint DESC, delay', [reachable_servers, server_id], (error, result) ->
throw error if error
for row in result.rows
regions[row.region_id].gateway = row.server_id
# temp hack before railgun-network protocol
#switch server_id
# when 2, 8
# servers[21].next_hop = 20
# servers[22].next_hop = 20
# servers[23].next_hop = 20
# regions[0].gateway = 4
# regions[1].gateway = 9
# when 1, 9
# servers[20].next_hop = 8
# servers[21].next_hop = 8
# servers[22].next_hop = 8
# servers[23].next_hop = 8
# when 6
# regions[0].gateway=5
# regions[1].gateway=9
done()
callback servers, regions
#!/usr/bin/env bash
set -o errexit
set -o allexport
source /etc/railgun/profile
set +o allexport
if grep -q $1 /etc/railgun/hacks.csv; then
sed -i "s/$1.*/$1,$2/" /etc/railgun/hacks.csv
else
echo $1,$2 >> /etc/railgun/hacks.csv
fi
gateway=$(awk -F, "\$1 == $2 { print \$2 }" /etc/railgun/regions.csv)
next_hop=$(awk -F, "\$1 == $2 { print \$3 }" /etc/railgun/regions.csv)
if [ -z "${gateway}" ] || [ -z "${next_hop}" ]; then
echo "can't find route for region$2"
exit 1
fi
if [ "${gateway}" = "${RAILGUN_ID}" ]; then
ip route replace $1 via ${RAILGUN_GATEWAY} table 101
else
ip route replace $1 dev railgun${next_hop} src ${RAILGUN_ADDRESS} realm ${gateway} advmss 1360 table 101
fi
*filter
:INPUT ACCEPT [1009:192504]
:FORWARD ACCEPT [25:3510]
:OUTPUT ACCEPT [1111:239704]
-A INPUT -m string --string "torrent" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "BitTorrent" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "peer_id=" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "info_hash" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "find_node" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "get_peers" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "announce" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "announce_peers" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A INPUT -m string --string "torrent" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "BitTorrent" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "peer_id=" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "info_hash" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "find_node" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "get_peers" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "announce" --algo kmp --to 65535 -j DROP
-A INPUT -m string --string "announce_peers" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "torrent" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "BitTorrent" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "peer_id=" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "info_hash" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "find_node" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "get_peers" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "announce" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "announce_peers" --algo kmp --to 65535 -j LOG --log-prefix "iptables DHT: " --log-level 7
-A FORWARD -m string --string "torrent" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "BitTorrent" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "peer_id=" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "info_hash" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "find_node" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "get_peers" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "announce" --algo kmp --to 65535 -j DROP
-A FORWARD -m string --string "announce_peers" --algo kmp --to 65535 -j DROP
COMMIT
# Completed on Thu Feb 4 08:05:19 2016
# Generated by iptables-save v1.4.21 on Thu Feb 4 08:05:19 2016
*nat
-A POSTROUTING -s 172.16.0.0/12 ! -o docker0 -j MASQUERADE
-A PREROUTING -p tcp -m set --match-set block_ip src -j REDIRECT --to-ports 3101
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -m multiport --dports 22,450,3000,1723,5001,5201 -j ACCEPT
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -m set --match-set ports1 dst -j REDIRECT --to-ports 3128
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -m set --match-set ports2 dst -j REDIRECT --to-ports 3129
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -m set --match-set ports3 dst -j REDIRECT --to-ports 1080
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -m set --match-set ports4 dst -j ACCEPT
-A PREROUTING -i ${RAILGUN_INTERFACE} -p tcp -m addrtype --dst-type LOCAL -j REDIRECT --to-ports 3100
# socks5 dns hack
-A OUTPUT -o ${RAILGUN_INTERFACE} -p udp --dport 53 -m owner --uid-owner proxy -j REDIRECT --to-ports 53
-A POSTROUTING -o ${RAILGUN_INTERFACE} -s 10.0.0.0/8 -j SNAT --to-source ${RAILGUN_PRIVATE_ADDRESS}
COMMIT
*mangle
-A FORWARD -s 172.16.0.0/12 -j ACCEPT
-A PREROUTING -s ${RAILGUN_ADDRESS}/16 ! -d 10.0.0.0/8 -p tcp -m multiport --dports 9300,9301,9400 -j MARK --set-xmark 0x1/0xffffffff
-A PREROUTING -s ${RAILGUN_ADDRESS}/16 ! -d 10.0.0.0/8 -p tcp -m multiport --dports 9300,9301,9400 -j ACCEPT
-A PREROUTING -s ${RAILGUN_ADDRESS}/16 -p tcp -m addrtype ! --dst-type LOCAL -j TPROXY --on-port 5000 --on-ip 0.0.0.0 --tproxy-mark 0x3
-A PREROUTING -s ${RAILGUN_ADDRESS}/16 ! -p tcp -j MARK --set-mark 0x1
-A INPUT -s ${RAILGUN_ADDRESS}/16 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1301:1536 -j TCPMSS --set-mss 1300
-A OUTPUT -d ${RAILGUN_ADDRESS}/16 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1301:1536 -j TCPMSS --set-mss 1300
-A POSTROUTING -o ${RAILGUN_INTERFACE} -j TOS --set-tos 0x3c
COMMIT
db = require './db'
protocol = require './protocol'
route = require './route'
server_id = parseInt process.env.RAILGUN_ID
db.init server_id, process.env.RAILGUN_DATABASE, (servers, regions)->
console.log "loaded #{Object.keys(servers).length} servers and #{Object.keys(regions).length} regions"
route.init server_id, servers, regions, ->
protocol.init server_id, servers, regions, route.update
{
"name": "railgun-network-client",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "railgun-network-client",
"version": "1.0.0",
"devDependencies": {
"@types/node": "^14.14.14",
"ts-node": "^9.1.1"
}
},
"node_modules/@types/node": {
"version": "14.14.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz",
"integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==",
"dev": true
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"dev": true,
"dependencies": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"typescript": ">=2.7"
}
},
"node_modules/typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"dev": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
"@types/node": {
"version": "14.14.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz",
"integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==",
"dev": true
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"ts-node": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
"dev": true,
"requires": {
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"source-map-support": "^0.5.17",
"yn": "3.1.1"
}
},
"typescript": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
"dev": true,
"peer": true
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}
......@@ -2,23 +2,12 @@
"name": "railgun-network-client",
"version": "1.0.0",
"description": "",
"scripts":{
"start": "coffee main.coffee"
"scripts": {
"start": "ts-node src/main.ts"
},
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/railgun-accelerator/railgun-network-client.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/railgun-accelerator/railgun-network-client/issues"
},
"homepage": "https://github.com/railgun-accelerator/railgun-network-client#readme",
"dependencies": {
"pg": "^6",
"csv": "latest",
"coffee-script": "latest"
"devDependencies": {
"@types/node": "^14.14.14",
"ts-node": "^9.1.1"
}
}
{
"id": 3,
"peers": [
{
"id": 23,
"address": "10.200.23.3",
"subnets": ["10.198.0.25"],
"mark": 18003
}
]
}
[Unit]
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/srv/railgun/network/start.sh
EnvironmentFile=/etc/railgun/profile
WorkingDirectory=/srv/railgun/network
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
fs = require 'fs'
child_process = require 'child_process'
csv = require 'csv'
ip_exec = (commands, force=false, callback)->
args = ['-batch', '-']
if force
args.push '-force'
child = child_process.spawn 'ip', args, stdio: ['pipe', process.stdout, process.stderr]
child.on 'close', callback
child.stdin.end commands.join('\n') + "\n"
iptables_exec = (commands, noflush = false, callback )->
args = []
if noflush
args.push '--noflush'
child = child_process.execFile 'iptables-restore', args, stdio: ['pipe', process.stdout, process.stderr]
result = (["*#{table}"].concat(rules, 'COMMIT').join("\n") for table, rules of commands when rules.length > 0).join("\n") + "\n"
child.on 'close', callback
child.stdin.end result
exec = (ipforce, ip, iptables)->
ip_exec ipforce, true, (code)->
console.log code
ip_exec ip, false, (code)->
console.log code
iptables_exec iptables, true, (code)->
console.log code
process.exit()
module.exports =
init: (server_id, servers, regions)->
ip = []
ipforce = []
iptables = {nat: [], mangle: [], filter: []}
# fwmark 0x1 / 本机发出 tos 0x4 源站选路
# fwmark 0x2 连接保持
# fwmark 0x3 TPROXY
# table 101 源站选路
# table 102 连接保持
# table 103 TPROXY
ipforce.push "rule del pref 200"
ipforce.push "rule del pref 400"
ipforce.push "rule del pref 401"
ipforce.push "rule del pref 402"
ipforce.push "rule del pref 403"
ipforce.push "rule del pref 404"
ipforce.push "rule del pref 405"
ip.push "rule add pref 200 fwmark 0x3 lookup 103" # TPROXY
ip.push "rule add pref 400 to #{servers[server_id].network} lookup main" # 到自己 VPN 内网
ip.push "rule add pref 401 fwmark 0x2 lookup 102" # 连接保持
ip.push "rule add pref 402 fwmark 0x1 lookup 101" # 源站选路
ip.push "rule add pref 403 iif lo tos 4 lookup 101" # 源站选路
ip.push "rule add pref 404 iif lo lookup main" # 除源站选路外, 本机发出的其他报文, 不进行路由
ip.push "rule add pref 405 to 10.0.0.0/8 lookup 101" # 其他转发至内网的报文
ip.push "route flush table 101"
ip.push "route flush table 102"
ip.push "route replace local default dev lo table 103"
#console.log servers
for i, server of servers when server.id != server_id
# 相邻的节点建立 tunnel
if server.link? and server.link != 'direct'
ipforce.push "link del railgun#{server.id}"
# mode = null
# encap = null
# sport = null
# dport = null
[mode, encap, sport, dport] = server.link.split '-'
if encap
if encap == 'gue'
ipproto='gue'
else if mode == 'ipip'
ipproto='ipproto 4'
else if mode == 'gre'
ipproto='ipproto 47'
ipforce.push "fou del port #{dport}"
ipforce.push "fou add port #{dport} #{ipproto}"
ip.push "link add railgun#{server.id} type #{mode} remote #{server.public_address} ttl 64 encap #{encap} encap-sport #{sport} encap-dport #{dport}"
else
ip.push "link add railgun#{server.id} type #{mode} remote #{server.public_address} ttl 64"
ip.push "addr add dev railgun#{server.id} #{process.env.RAILGUN_ADDRESS} peer #{server.address}"
ip.push "link set dev railgun#{server.id} up"
else if server.next_hop?
# 为了调试方便, 把不相邻但是可达的节点在 main 做个路由, 这个生产不会使用.
ip.push "route replace #{server.network} dev railgun#{server.next_hop} src #{servers[server_id].host}"
# 可达节点的路由
if server.next_hop?
ip.push "route replace #{server.network} dev railgun#{server.next_hop} src #{servers[server_id].host} table 101"
ip.push "route replace tos #{server.tos} default dev railgun#{server.next_hop} src #{servers[server_id].host} table 102" if server.tos?
# 连接保持
if server.tos?
iptables.mangle.push "-A FORWARD -m connmark --mark 0 -m realm --realm #{server.id} -j CONNMARK --set-mark #{server.id}"
iptables.mangle.push "-A FORWARD -m connmark --mark #{server.id} -j TOS --set-tos #{server.tos}"
iptables.mangle.push "-A FORWARD -m connmark --mark #{server.id} -j MARK --set-mark 0x2"
iptables.mangle.push "-A OUTPUT -m connmark --mark 0 -m realm --realm #{server.id} -j CONNMARK --set-mark #{server.id}"
iptables.mangle.push "-A OUTPUT -m connmark --mark #{server.id} -j TOS --set-tos #{server.tos}"
iptables.mangle.push "-A OUTPUT -m connmark --mark #{server.id} -j MARK --set-mark 0x2"
for i, region of regions when region.gateway?
for address in region.addresses
if region.gateway == server_id
ip.push "route add #{address} via #{process.env.RAILGUN_GATEWAY} dev #{process.env.RAILGUN_INTERFACE} table 101"
else
ip.push "route add #{address} advmss 1360 dev railgun#{servers[region.gateway].next_hop} src #{servers[server_id].host} realm #{region.gateway} table 101"
# hacks
csv.stringify ([region.id, region.gateway, servers[region.gateway].next_hop] for i, region of regions when region.gateway?), (error, data)->
throw error if error
fs.writeFile '/etc/railgun/regions.csv', data, (error)->
throw error if error
fs.readFile '/etc/railgun/hacks.csv', (error, data)->
if data
csv.parse data, (error, data)->
if data
for hack in data
[address,region_id] = hack
region = regions[region_id]
if region and region.gateway?
if region.gateway == server_id
ip.push "route replace #{address} via #{process.env.RAILGUN_GATEWAY} table 101"
else
ip.push "route add #{address} advmss 1360 dev railgun#{servers[region.gateway].next_hop} src #{servers[server_id].host} realm #{region.gateway} table 101"
exec(ipforce, ip, iptables)
else
exec(ipforce, ip, iptables)
import config from '../config.json';
import { Socket } from 'dgram';
import { PeerMeasure } from './protocol';
export interface PeerHello {
id: number;
seq: number;
time: number;
}
export interface PeerConfig {
id: number;
address: string;
subnets: string[];
}
export class Peer implements PeerHello, PeerMeasure, PeerConfig {
id: number;
address: string;
subnets: string[];
delay: number = 0;
reliability: number = 0;
seq: number = 0;
time: number = 0;
constructor(config: PeerConfig) {
Object.assign(this, config);
}
reset() {
this.delay = 0;
this.reliability = 0;
this.seq = 0;
this.time = 0;
}
onMessage(data: PeerHello) {
if (data.seq == 0 || data.seq < this.seq - config.timeout || data.seq > this.seq + config.timeout) {
// 收到 seq = 0 或 seq 与之前差距较大,就 reset
this.reset();
} else if (data.seq <= this.seq) {
// 收到 seq 比已知略小的,忽略
return;
}
// 全新或者 seq 比已知略大。
const step = data.seq - this.seq;
const delay = Date.now() - data.time;
this.reliability = (this.reliability * (config.timeout - step)) / config.timeout + 1 / config.timeout;
this.delay = (this.delay * (config.timeout - 1)) / config.timeout + delay / config.timeout;
this.seq = data.seq;
this.time = data.time;
}
update(socket: Socket, self: PeerHello) {
if (this.reliability == 0) {
return;
}
// 有几个包没到
const step = Math.floor((self.time - this.time + this.delay - config.interval) / config.interval);
if (step > config.timeout) {
this.reset();
return;
}
// if(self.time - this.time )
socket.send(JSON.stringify(self), config.port, this.address, (error, bytes) => error && console.warn(error));
}
// pickServer(): PeerMeasure {
// const { id, delay, reliability } = this;
// return { id, delay, reliability };
// }
}
import { Peer } from './Peer';
const tableOffset = 10000;
const groupTableOffset = 11000;
export class RouteWriter {
routes: Route[];
constructor() {}
reset() {
// TODO: 所有 neighbor 重设为直连,不是 neighbor 的设为 unreachable
// 只是记下来,commit的时候再写入
}
add(to: Peer, via: Peer) {
for (const subnet of to.subnets) {
this.routes.push({ to: subnet, via: via.address, table: 'main' });
}
this.routes.push({ to: 'default', via: via.address, table: tableOffset + to.id });
}
// groupAdd(to: Peer, via: Peer) {
// this.routes.push({ to: 'default', via: via.address, table: groupTableOffset + to.id });
// }
commit() {
// TODO: 写进系统,用 ip -batch
}
}
export interface Route {
to: string;
via: string;
table: number | string;
}
import { RouteWriter } from './RouteWriter';
import { Peer, PeerHello, PeerMeasure } from './Peer';
import { Socket } from 'dgram';
import config from '../config.json';
export class Server {
ack = 0;
onMessage(data: ServerMessage, socket: Socket, self: PeerHello, peers: Map<number, Peer>) {
if (data.seq && this.ack != data.seq) {
return;
}
const routeWriter = new RouteWriter();
if (data.seq === 0) {
routeWriter.reset();
}
for (const [to, via] of Object.entries(data.routes).map(([to, via]) => [peers.get(parseInt(to)), peers.get(via)])) {
routeWriter.add(to, via);
}
// for (const [to, via] of Object.entries(data.groups).map(([to, via]) => [peers.get(parseInt(to)), peers.get(via)])) {
// routeWriter.groupAdd(to, via);
// }
this.ack = data.seq + 1;
socket.send(
JSON.stringify({
id: self.id,
ack: this.ack,
}),
config.server_port,
config.server_address
);
}
update(socket: Socket, self: PeerHello, peers: Map<number, Peer>) {
const p: PeerMeasure[] = [];
for (const peer of peers.values()) {
if (peer.reliability === 0) {
continue;
}
// 有几个包没到
const step = Math.max(0, Math.floor((self.time - peer.time + peer.delay - config.interval / 2) / config.interval));
if (step >= config.timeout) {
peer.reset();
continue;
}
const reliability = (peer.reliability * (config.timeout - step)) / config.timeout;
const { id, delay } = peer;
p.push({ id, delay, reliability });
}
socket.send(JSON.stringify({ id: self.id, ack: this.ack, peers: p }), config.server_port, config.server_address);
}
}
export interface ServerMessage {
seq: number;
routes: { [to: number]: number };
groups: { [to: number]: number };
}
import dgram from 'dgram';
import assert from 'assert';
import { Peer, PeerHello } from './Peer';
import config from '../config.json';
import { Server, ServerMessage } from './Server';
import { id, peers as peersConfig } from '../peers.json';
const self: PeerHello = { id, seq: 0, time: 0 };
const server = new Server();
const peers = new Map<number, Peer>();
for (const peer of peersConfig) {
peers.set(peer.id, new Peer(peer));
}
const socket = dgram.createSocket('udp4');
socket.on('message', (msg, rinfo) => {
try {
if (rinfo.address == config.server_address && rinfo.port == config.server_port) {
// from server
const hello: ServerMessage = JSON.parse(msg.toString());
server.onMessage(hello, socket, self, peers);
} else {
// from client
const hello: PeerHello = JSON.parse(msg.toString());
assert.ok(hello.id);
const peer = peers.get(hello.id);
assert.ok(peer && rinfo.address == peer.address && rinfo.port == config.port);
peer.onMessage(hello);
}
} catch (e) {
console.warn(e);
}
});
socket.on('listening', () => {
const address = socket.address();
console.log(`listening ${address.address}:${address.port}`);
});
socket.bind(config.port);
setInterval(() => {
self.time = Date.now();
for (const peer of peers.values()) {
peer.update(socket, self);
}
server.update(socket, self, peers);
self.seq++;
}, config.interval);
export interface PeerMeasure {
id: number;
delay: number;
reliability: number;
}
export interface ServerMessage {
id: number;
ack: number;
peers: PeerMeasure[];
}
#!/usr/bin/env bash
set -o errexit
echo 'modprobe fou...'
modprobe fou
echo 'ipsec...'
bash /etc/railgun/ipsec.sh
echo 'ipset...'
ipset create -exist ports1 bitmap:port range 10000-32767
ipset create -exist ports2 bitmap:port range 10000-32767
ipset create -exist ports3 bitmap:port range 10000-32767
ipset create -exist ports4 bitmap:port range 10000-32767
ipset create -exist block_ip hash:ip
echo 'iptables...'
envsubst < iptables-rules | iptables-restore
if [ -n "${RAILGUN_TOS}" ]; then
iptables -t mangle -A PREROUTING -s 10.0.0.0/12 -p tcp -m addrtype ! --dst-type LOCAL -m tos --tos ${RAILGUN_TOS} -j TPROXY --on-port 5000 --on-ip 0.0.0.0 --tproxy-mark 0x3
fi
echo 'strongswan hack'
ip route replace 10.${RAILGUN_ID}.16.0/20 via ${RAILGUN_GATEWAY}
ip route replace 10.${RAILGUN_ID}.32.0/20 via ${RAILGUN_GATEWAY}
ip route replace 10.${RAILGUN_ID}.48.0/20 via ${RAILGUN_GATEWAY}
ip route replace 10.${RAILGUN_ID}.64.0/20 via ${RAILGUN_GATEWAY}
ip route replace 10.${RAILGUN_ID}.96.0/20 via ${RAILGUN_GATEWAY}
echo 'network...'
npm start
ip route flush cache
sleep 1000d
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"lib": []
},
"exclude": [
"node_modules"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment