Skip to content

Commit 23d0b01

Browse files
committed
crc refactoring
1 parent 68b4c80 commit 23d0b01

10 files changed

+263
-214
lines changed

README.md

+36-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Module implements sockets interface ([RFC6458]) in Node.js [Net] API.
88
99
Implementation of [RFC4960] is currently incomplete, use at your own risk!
1010

11+
Also raw-socket module seem to sometimes loose ip packets (extremely rarely). This is recovered by SCTP mechanisms of retransmission.
12+
1113
## Disable LK-SCTP
1214

1315
On Linux LK-SCTP should be disabled, because it conflicts with any other implementation. To prevent the "sctp" kernel module from being loaded, add the following line to a file in the directory "/etc/modprobe.d":
@@ -64,10 +66,43 @@ extra socket options:
6466
* options.OS - requested outbound streams (integer, default: 2)
6567
* options.logger - logger object for debugging purposes (e.g. console or log4js' logger)
6668

69+
**sctp.defaults(options)**
70+
71+
Function sets default module default parameters. Names follow net.sctp conventions.
72+
See `sysctl -a | grep sctp`. Example:
73+
74+
```
75+
sctp.defaults({
76+
rto_initial: 500,
77+
rto_min: 300,
78+
rto_max: 1000,
79+
sack_timeout: 150,
80+
sack_freq: 2,
81+
})
82+
```
83+
Returns current default parameters.
84+
6785
**sctp.protocol**
6886

6987
sctp.protocol is a dictionary object with [SCTP Payload Protocol Identifiers][ppid]
7088

89+
```
90+
{
91+
SCTP: 0,
92+
IUA: 1,
93+
M2UA: 2,
94+
M3UA: 3,
95+
SUA: 4,
96+
M2PA: 5,
97+
V5UA: 6,
98+
H248: 7,
99+
BICC: 8,
100+
...
101+
}
102+
```
103+
104+
See example below.
105+
71106
**socket.SCTP_DEFAULT_SEND_PARAM(options)**
72107

73108
Set socket options related to write operations. Argument 'options' is an object with the following keys (all optional):
@@ -112,8 +147,7 @@ socket.on('data', function (buffer) {
112147

113148
## Credits
114149
* Inspiration and some ideas are taken from [smpp] module
115-
* crc32c function is from fast-crc32c module
116-
* SerialNumber is from serial-arithmetic module
150+
* CRC algorithm ported from https://pycrc.org/
117151

118152
[raw-socket]: https://www.npmjs.com/package/raw-socket
119153
[Net]: https://nodejs.org/api/net.html

lib/association.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Association extends EventEmitter {
3232
}
3333

3434
this.log('trace', 'create association', options)
35+
3536
// todo provide also way to iterate
3637
endpoint.associations[this.remoteAddress + ':' + this.remotePort] = this
3738

@@ -1315,12 +1316,12 @@ class Association extends EventEmitter {
13151316
-> result
13161317
*/
13171318

1318-
if (this.state === 'CLOSED') {
1319+
this.log('trace', 'API SHUTDOWN in state', this.state)
1320+
if (this.state !== 'ESTABLISHED') {
1321+
this.log('trace', 'just destroy association')
13191322
this._destroy()
13201323
return
13211324
}
1322-
if (this.state === 'SHUTDOWN-RECEIVED') return
1323-
this.log('trace', 'API SHUTDOWN')
13241325
this.state = 'SHUTDOWN-PENDING'
13251326
/*
13261327
Upon receipt of the SHUTDOWN primitive from its upper layer, the

lib/crc.js

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
3+
https://en.wikipedia.org/wiki/Cyclic_redundancy_check
4+
5+
Algorithms ported from https://pycrc.org/
6+
7+
*/
8+
9+
class CRC {
10+
constructor(width, poly, xor_in, xor_out, reflect) {
11+
this.width = width
12+
this.poly = poly
13+
this.xor_in = xor_in
14+
this.xor_out = xor_out
15+
this.reflect = reflect
16+
this.msb_mask = 1 << (this.width - 1)
17+
this.mask = ((this.msb_mask - 1) << 1) | 1
18+
this.crc_shift = this.width < 8 ? 8 - this.width : 0
19+
let table = this.gen_table()
20+
this.table = table
21+
22+
// optimized for crc32c
23+
if (width === 32 && reflect && xor_in === 0xffffffff && xor_out === 0xffffffff) {
24+
this.calculate = function (buffer) {
25+
buffer = preprocess(buffer)
26+
let crc = -1
27+
for (let i = 0; i < buffer.length; i++)
28+
crc = table[(crc ^ buffer[i]) & 0xff] ^ (crc >>> 8)
29+
return (crc ^ -1) >>> 0
30+
}
31+
}
32+
}
33+
34+
calculate(buffer) {
35+
buffer = preprocess(buffer)
36+
let crc
37+
if (this.reflect) {
38+
crc = this.xor_in === 0xffffffff ? -1 : reflect(this.xor_in, this.width)
39+
for (let i = 0; i < buffer.length; i++) {
40+
let key = (crc ^ buffer[i]) & 0xff
41+
crc = (this.table[key] ^ (crc >>> 8)) & this.mask
42+
}
43+
} else {
44+
crc = this.xor_in << this.crc_shift
45+
for (let i = 0; i < buffer.length; i++) {
46+
let key = ((crc >> (this.width - 8 + this.crc_shift)) ^ buffer[i]) & 0xff
47+
crc <<= 8 - this.crc_shift
48+
crc ^= this.table[key] << this.crc_shift
49+
crc &= this.mask << this.crc_shift
50+
}
51+
crc >>= this.crc_shift
52+
}
53+
crc ^= this.xor_out
54+
return crc >>> 0
55+
}
56+
57+
calculate_no_table(buffer) {
58+
buffer = preprocess(buffer)
59+
let crc = this.xor_in
60+
for (let i = 0; i < buffer.length; i++) {
61+
let octet = buffer[i]
62+
if (this.reflect) octet = reflect(octet, 8)
63+
for (let i = 0; i < 8; i++) {
64+
let topbit = crc & this.msb_mask
65+
if (octet & (0x80 >> i)) topbit ^= this.msb_mask
66+
crc <<= 1
67+
if (topbit) crc ^= this.poly
68+
}
69+
crc &= this.mask
70+
}
71+
if (this.reflect) crc = reflect(crc, this.width)
72+
crc ^= this.xor_out
73+
return crc >>> 0
74+
}
75+
76+
gen_table() {
77+
let table_length = 256
78+
let table = []
79+
for (let i = 0; i < table_length; i++) {
80+
let reg = i
81+
if (this.reflect) reg = reflect(reg, 8)
82+
reg = reg << (this.width - 8 + this.crc_shift)
83+
for (let j = 0; j < 8; j++) {
84+
if ((reg & (this.msb_mask << this.crc_shift)) !== 0) {
85+
reg <<= 1
86+
reg ^= this.poly << this.crc_shift
87+
} else {
88+
reg <<= 1
89+
}
90+
}
91+
if (this.reflect) reg = reflect(reg >> this.crc_shift, this.width) << this.crc_shift
92+
reg = (reg >> this.crc_shift) & this.mask
93+
table[i] = reg >>> 0
94+
}
95+
return table
96+
}
97+
98+
print() {
99+
let table = this.table
100+
let digits = Math.ceil(this.width / 4)
101+
let shift = (digits < 4) ? 4 : 3
102+
let rows = table.length >> shift
103+
let columns = 1 << shift
104+
let result = ''
105+
for (let r = 0; r < rows; r++) {
106+
let row = ''
107+
for (let c = 0; c < columns; c++) {
108+
let val = table[r << shift | c]
109+
val = '000000000' + val.toString(16)
110+
val = val.substr(val.length - digits)
111+
row += '0x' + val + ', '
112+
}
113+
result += ' ' + row + '\n'
114+
}
115+
result = '[\n' + result.slice(0, -3) + '\n]'
116+
return result
117+
}
118+
119+
}
120+
121+
122+
function preprocess(data) {
123+
if (Buffer.isBuffer(data)) return data
124+
switch (typeof data) {
125+
case 'number':
126+
let buffer = Buffer.alloc(4)
127+
buffer.writeUInt32BE(data)
128+
return buffer
129+
case 'string':
130+
return Buffer.from(data)
131+
default:
132+
throw new Error()
133+
}
134+
}
135+
136+
137+
function reflect(data, width) {
138+
let res = 0
139+
for (let i = 0; i < width; i++) {
140+
res = res << 1 | data & 1
141+
data >>= 1
142+
}
143+
return res
144+
}
145+
146+
147+
module.exports = {
148+
CRC,
149+
crc6: new CRC(6, 0x2F),
150+
crc8: new CRC(8, 0x07),
151+
crc10: new CRC(10, 0x233),
152+
crc32: new CRC(32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true),
153+
crc32c: new CRC(32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true)
154+
}

lib/defs.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ let _ = require('lodash')
88
let ip = require('ip')
99

1010
let net_sctp = {
11-
G: 10,
11+
G: 50, // granularity
1212
RWND: 256000,
1313
rto_initial: 3000,
1414
rto_min: 1000,
@@ -25,6 +25,42 @@ let net_sctp = {
2525
sack_freq: 2
2626
}
2727

28+
/*
29+
30+
todo
31+
sysctl -a | grep sctp
32+
33+
net.sctp.addip_enable = 0
34+
net.sctp.addip_noauth_enable = 0
35+
net.sctp.addr_scope_policy = 1
36+
net.sctp.association_max_retrans = 10
37+
net.sctp.auth_enable = 0
38+
net.sctp.cookie_hmac_alg = sha1
39+
net.sctp.cookie_preserve_enable = 1
40+
net.sctp.default_auto_asconf = 0
41+
net.sctp.hb_interval = 30000
42+
net.sctp.max_autoclose = 2147483
43+
net.sctp.max_burst = 4
44+
net.sctp.max_init_retransmits = 8
45+
net.sctp.path_max_retrans = 5
46+
net.sctp.pf_retrans = 0
47+
net.sctp.prsctp_enable = 1
48+
net.sctp.rcvbuf_policy = 0
49+
net.sctp.rto_alpha_exp_divisor = 3
50+
net.sctp.rto_beta_exp_divisor = 2
51+
net.sctp.rto_initial = 3000
52+
net.sctp.rto_max = 60000
53+
net.sctp.rto_min = 1000
54+
net.sctp.rwnd_update_shift = 4
55+
net.sctp.sack_timeout = 200
56+
net.sctp.sctp_mem = 42486 56648 84972
57+
net.sctp.sctp_rmem = 4096 865500 1812736
58+
net.sctp.sctp_wmem = 4096 16384 1812736
59+
net.sctp.sndbuf_policy = 0
60+
net.sctp.valid_cookie_life = 60000
61+
62+
*/
63+
2864
let types = {
2965
int8: {
3066
read: function (buffer, offset) {

lib/index.js

+11-13
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,16 @@ function connect(options, connectListener) {
2929
return socket
3030
}
3131

32-
/*
33-
module.exports.net_sctp = function (params) {
34-
Object.assign(defs.net_sctp, params)
35-
if (defs.net_sctp.sack_timeout > 500) defs.net_sctp.sack_timeout = 500
36-
if (defs.net_sctp.RWND < 1500) defs.net_sctp.RWND = 1500
37-
}
38-
*/
39-
40-
function SCTP_RTOINFO(params) {
41-
defs.net_sctp.rto_initial = params.rto_initial || defs.net_sctp.rto_initial
42-
defs.net_sctp.rto_min = params.rto_min || defs.net_sctp.rto_min
43-
defs.net_sctp.rto_max = params.rto_max || defs.net_sctp.rto_max
32+
function defaults(params) {
33+
params = params || {}
34+
for (let param in defs.net_sctp) {
35+
if (param in params) {
36+
defs.net_sctp[param] = params[param]
37+
}
38+
}
39+
if (defs.net_sctp.sack_timeout > 500) defs.net_sctp.sack_timeout = 500
40+
if (defs.net_sctp.RWND < 1500) defs.net_sctp.RWND = 1500
41+
return defs.net_sctp
4442
}
4543

4644

@@ -53,5 +51,5 @@ module.exports = {
5351
Server,
5452
PPID: defs.PPID,
5553
protocol: defs.PPID,
56-
SCTP_RTOINFO
54+
defaults
5755
}

0 commit comments

Comments
 (0)