node-netlink 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +90 -0
- package/LICENSE +479 -649
- package/README.md +1 -0
- package/lib/error.js +50 -0
- package/lib/index.js +259 -0
- package/lib/parser.js +55 -0
- package/lib/structures.js +21 -0
- package/package.json +11 -6
- package/samples/test.js +11 -0
- package/test/base.js +8 -4
- package/.npmignore +0 -61
- package/binding.gyp +0 -11
- package/index.js +0 -150
- package/native/netlink.cc +0 -360
package/README.md
CHANGED
package/lib/error.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const knownErrors = {
|
|
2
|
+
EPERM: {
|
|
3
|
+
code: 1,
|
|
4
|
+
message: "Operation not permitted",
|
|
5
|
+
},
|
|
6
|
+
ENOENT: {
|
|
7
|
+
code: 2,
|
|
8
|
+
message: "No such file or directory",
|
|
9
|
+
},
|
|
10
|
+
EAGAIN: {
|
|
11
|
+
code: 11,
|
|
12
|
+
message: "Resource temporarily unavailable",
|
|
13
|
+
},
|
|
14
|
+
EEXIST: {
|
|
15
|
+
code: 17,
|
|
16
|
+
message: "File already exists",
|
|
17
|
+
},
|
|
18
|
+
ENODEV: {
|
|
19
|
+
code: 19,
|
|
20
|
+
message: "No such device",
|
|
21
|
+
},
|
|
22
|
+
EINVAL: {
|
|
23
|
+
code: 22,
|
|
24
|
+
message: "Invalid argument",
|
|
25
|
+
},
|
|
26
|
+
ENOTSUP: {
|
|
27
|
+
code: 48,
|
|
28
|
+
message: "Operation not supported",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const createFromErrorCode = ({ errorCode, message }) => {
|
|
33
|
+
const knownErrorKey = Object.keys(knownErrors).find((key) => {
|
|
34
|
+
return knownErrors[key].code === errorCode;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (knownErrorKey) {
|
|
38
|
+
const knownError = knownErrors[knownErrorKey];
|
|
39
|
+
const errorObject = new Error(`${message ? `${message}: ` : ""}${knownError.message}`);
|
|
40
|
+
errorObject.code = knownError.code;
|
|
41
|
+
errorObject.errno = errorCode;
|
|
42
|
+
return errorObject;
|
|
43
|
+
} else {
|
|
44
|
+
return new Error(`${message ? `${message}: ` : ""}unknown error code ${errorCode}`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default {
|
|
49
|
+
createFromErrorCode,
|
|
50
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import socket from "po6-socket";
|
|
2
|
+
import EventEmitter from "events";
|
|
3
|
+
|
|
4
|
+
import structures from "./structures.js";
|
|
5
|
+
import netlinkParser from "./parser.js";
|
|
6
|
+
import errorFactory from "./error.js";
|
|
7
|
+
|
|
8
|
+
const AF_NETLINK = 16n;
|
|
9
|
+
const SOCK_DGRAM = 2n;
|
|
10
|
+
|
|
11
|
+
const NLM_F_ECHO = 8n;
|
|
12
|
+
const NLM_F_REQUEST = 1n;
|
|
13
|
+
const NLM_F_MULTI = 2n;
|
|
14
|
+
const NLM_F_MATCH = 0x200n;
|
|
15
|
+
const NLM_F_DUMP = 0x300n;
|
|
16
|
+
const NLM_F_ACK = 4n;
|
|
17
|
+
const NLM_F_CREATE = 0x400n;
|
|
18
|
+
const NLM_F_EXCL = 0x200n;
|
|
19
|
+
|
|
20
|
+
const NLMSG_ERROR = 2n;
|
|
21
|
+
const NLMSG_DONE = 3n;
|
|
22
|
+
|
|
23
|
+
const open = ({ family, pid, groups }) => {
|
|
24
|
+
const emitter = new EventEmitter();
|
|
25
|
+
|
|
26
|
+
return socket
|
|
27
|
+
.create({
|
|
28
|
+
domain: AF_NETLINK,
|
|
29
|
+
type: SOCK_DGRAM,
|
|
30
|
+
protocol: BigInt(family),
|
|
31
|
+
})
|
|
32
|
+
.then((sock) => {
|
|
33
|
+
const netlinkAddress = structures.sockaddr_nl.format({
|
|
34
|
+
nl_family: AF_NETLINK,
|
|
35
|
+
nl_pid: BigInt(pid || 0),
|
|
36
|
+
nl_groups: BigInt(groups || 0),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const close = () => {
|
|
40
|
+
return sock.close();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
sock.on("message", ({ data }) => {
|
|
44
|
+
const parsedPackets = netlinkParser.parseMessage({ message: data });
|
|
45
|
+
|
|
46
|
+
parsedPackets.forEach((packet) => {
|
|
47
|
+
emitter.emit("message", packet);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let seq = 1n;
|
|
52
|
+
|
|
53
|
+
const send = ({ header: requestedHeader, payload }) => {
|
|
54
|
+
let header = Object.assign({}, requestedHeader);
|
|
55
|
+
|
|
56
|
+
if (header.nlmsg_seq === undefined) {
|
|
57
|
+
header = Object.assign({}, header, {
|
|
58
|
+
nlmsg_seq: seq,
|
|
59
|
+
});
|
|
60
|
+
seq += 1n;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (header.nlmsg_pid === undefined) {
|
|
64
|
+
header = Object.assign({}, header, {
|
|
65
|
+
// "nlmsg_pid": nl_pid
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
header = Object.assign({}, header, {
|
|
70
|
+
nlmsg_len: BigInt(structures.nlmsghdr.size) + BigInt(payload.length),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const headerAsBuf = structures.nlmsghdr.format(header);
|
|
74
|
+
|
|
75
|
+
const data = Buffer.concat([headerAsBuf, payload]);
|
|
76
|
+
|
|
77
|
+
const destBuffer = structures.sockaddr_nl.format({
|
|
78
|
+
nl_family: AF_NETLINK,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// TODO: flags
|
|
82
|
+
|
|
83
|
+
return sock.sendmsg({
|
|
84
|
+
data,
|
|
85
|
+
msghdr: {
|
|
86
|
+
msg_name: destBuffer,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let requested = {};
|
|
92
|
+
|
|
93
|
+
const maybeAppendMessageToResponse = ({ message }) => {
|
|
94
|
+
const { header } = message;
|
|
95
|
+
|
|
96
|
+
if (header.nlmsg_type === NLMSG_DONE) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
requested = {
|
|
101
|
+
[header.nlmsg_seq]: {
|
|
102
|
+
...requested[header.nlmsg_seq],
|
|
103
|
+
msgs: [...(requested[header.nlmsg_seq]?.msgs || []), message],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const maybeFinishRequest = ({ message }) => {
|
|
109
|
+
const { header } = message;
|
|
110
|
+
const req = requested[header.nlmsg_seq];
|
|
111
|
+
|
|
112
|
+
const isLast =
|
|
113
|
+
header.nlmsg_type === NLMSG_ERROR || header.nlmsg_type === NLMSG_DONE;
|
|
114
|
+
|
|
115
|
+
if (isLast) {
|
|
116
|
+
clearTimeout(req.schedule);
|
|
117
|
+
req.resolve(req.msgs);
|
|
118
|
+
|
|
119
|
+
const { [header.nlmsg_seq]: unused, ...other } = requested;
|
|
120
|
+
requested = other;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const processResponse = ({ message }) => {
|
|
125
|
+
const { header } = message;
|
|
126
|
+
const req = requested[header.nlmsg_seq];
|
|
127
|
+
|
|
128
|
+
if (!req) {
|
|
129
|
+
console.warn(
|
|
130
|
+
`could not find pending request for seq=${header.nlmsg_seq}`
|
|
131
|
+
);
|
|
132
|
+
console.warn(`nlmsg_type = ${header.nlmsg_type}`);
|
|
133
|
+
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
maybeAppendMessageToResponse({ message });
|
|
138
|
+
maybeFinishRequest({ message });
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
emitter.on("message", (message) => {
|
|
142
|
+
const { header } = message;
|
|
143
|
+
|
|
144
|
+
if (header.nlmsg_flags & NLM_F_REQUEST) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (header.nlmsg_seq === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
processResponse({ message });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const tryTalk = (message, { timeout = 10000 } = {}) => {
|
|
156
|
+
if (!(message.header.nlmsg_flags & NLM_F_ACK)) {
|
|
157
|
+
throw new Error(`talk only supports requests with NLM_F_ACK set`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return Promise.resolve()
|
|
161
|
+
.then(() => {
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
const header = Object.assign({}, message.header, {
|
|
164
|
+
nlmsg_seq: seq,
|
|
165
|
+
});
|
|
166
|
+
seq += 1n;
|
|
167
|
+
|
|
168
|
+
const schedule = setTimeout(() => {
|
|
169
|
+
const { [header.nlmsg_seq]: unused, ...other } = requested;
|
|
170
|
+
requested = other;
|
|
171
|
+
|
|
172
|
+
reject(new Error("timeout reached"));
|
|
173
|
+
}, timeout);
|
|
174
|
+
|
|
175
|
+
requested = {
|
|
176
|
+
...requested,
|
|
177
|
+
[header.nlmsg_seq]: {
|
|
178
|
+
msgs: [],
|
|
179
|
+
schedule,
|
|
180
|
+
resolve,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
send({
|
|
185
|
+
header,
|
|
186
|
+
payload: message.payload,
|
|
187
|
+
}).catch(reject);
|
|
188
|
+
});
|
|
189
|
+
})
|
|
190
|
+
.then((packets) => {
|
|
191
|
+
let errorCode = 0;
|
|
192
|
+
|
|
193
|
+
const packetsWithoutError = packets.filter((pkt) => {
|
|
194
|
+
if (pkt.header.nlmsg_type === NLMSG_ERROR) {
|
|
195
|
+
const packetErrorCode = pkt.payload.readInt32LE(0);
|
|
196
|
+
if (packetErrorCode !== 0) {
|
|
197
|
+
errorCode = -packetErrorCode;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
} else {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
errorCode,
|
|
207
|
+
packets: packetsWithoutError,
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const talk = (obj, options) => {
|
|
213
|
+
return tryTalk(obj, options).then(({ errorCode, packets }) => {
|
|
214
|
+
if (errorCode !== 0) {
|
|
215
|
+
throw errorFactory.createFromErrorCode({ errorCode });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return packets;
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return sock
|
|
223
|
+
.bind({
|
|
224
|
+
sockaddr: netlinkAddress,
|
|
225
|
+
})
|
|
226
|
+
.then(() => {
|
|
227
|
+
return sock.getsockname();
|
|
228
|
+
})
|
|
229
|
+
.then((actualAddressBuffer) => {
|
|
230
|
+
const { nl_pid } = structures.sockaddr_nl.parse(actualAddressBuffer);
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
nl_pid,
|
|
234
|
+
|
|
235
|
+
talk,
|
|
236
|
+
tryTalk,
|
|
237
|
+
close,
|
|
238
|
+
on: emitter.on.bind(emitter),
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export default {
|
|
245
|
+
NLM_F_ECHO,
|
|
246
|
+
NLM_F_REQUEST,
|
|
247
|
+
NLM_F_MULTI,
|
|
248
|
+
NLM_F_DUMP,
|
|
249
|
+
NLM_F_ACK,
|
|
250
|
+
NLM_F_CREATE,
|
|
251
|
+
NLM_F_EXCL,
|
|
252
|
+
NLM_F_MATCH,
|
|
253
|
+
|
|
254
|
+
NLMSG_DONE,
|
|
255
|
+
NLMSG_ERROR,
|
|
256
|
+
|
|
257
|
+
open,
|
|
258
|
+
createErrorFromErrorCode: errorFactory.createFromErrorCode,
|
|
259
|
+
};
|
package/lib/parser.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import structures from "./structures.js";
|
|
2
|
+
|
|
3
|
+
const align4 = (addr) => {
|
|
4
|
+
return (addr + 3) & ~3;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const parseNetlinkMessage = ({ message }) => {
|
|
8
|
+
const parsed = structures.nlmsghdr.parse(message);
|
|
9
|
+
|
|
10
|
+
const header = {
|
|
11
|
+
nlmsg_type: parsed.nlmsg_type,
|
|
12
|
+
nlmsg_flags: parsed.nlmsg_flags,
|
|
13
|
+
nlmsg_seq: parsed.nlmsg_seq,
|
|
14
|
+
nlmsg_pid: parsed.nlmsg_pid
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const payloadOffset = align4(structures.nlmsghdr.size);
|
|
18
|
+
const payloadLength = parsed.nlmsg_len - BigInt(payloadOffset);
|
|
19
|
+
|
|
20
|
+
if (payloadLength < 0) {
|
|
21
|
+
throw new Error("negative payload length, packet malformed");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const payload = message.slice(payloadOffset, payloadOffset + Number(payloadLength));
|
|
25
|
+
const space = align4(payloadOffset + payload.length);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
header,
|
|
29
|
+
payload,
|
|
30
|
+
space
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const parseMessage = ({ message }) => {
|
|
35
|
+
let result = [];
|
|
36
|
+
|
|
37
|
+
let remaining = message;
|
|
38
|
+
while (remaining.length > 0) {
|
|
39
|
+
const { header, payload, space } = parseNetlinkMessage({ message: remaining });
|
|
40
|
+
const parsed = {
|
|
41
|
+
header,
|
|
42
|
+
payload
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
result = [...result, parsed ];
|
|
46
|
+
|
|
47
|
+
remaining = remaining.slice(space);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default {
|
|
54
|
+
parseMessage
|
|
55
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import struct from "ya-struct";
|
|
2
|
+
|
|
3
|
+
const sockaddr_nl = struct.define(({ field }) => {
|
|
4
|
+
field.UInt16("nl_family");
|
|
5
|
+
field.UInt16("nl_pad");
|
|
6
|
+
field.UInt32("nl_pid");
|
|
7
|
+
field.UInt32("nl_groups");
|
|
8
|
+
}).forHost();
|
|
9
|
+
|
|
10
|
+
const nlmsghdr = struct.define(({ field }) => {
|
|
11
|
+
field.UInt32("nlmsg_len");
|
|
12
|
+
field.UInt16("nlmsg_type");
|
|
13
|
+
field.UInt16("nlmsg_flags");
|
|
14
|
+
field.UInt32("nlmsg_seq");
|
|
15
|
+
field.UInt32("nlmsg_pid");
|
|
16
|
+
}).forHost();
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
sockaddr_nl,
|
|
20
|
+
nlmsghdr
|
|
21
|
+
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-netlink",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.2",
|
|
4
5
|
"description": "netlink bindings for node.js",
|
|
5
|
-
"main": "index.js",
|
|
6
|
+
"main": "lib/index.js",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"test": "mocha"
|
|
8
|
+
"test": "c8 --reporter lcov --reporter html --reporter text mocha",
|
|
9
|
+
"eslint": "eslint ."
|
|
8
10
|
},
|
|
9
11
|
"dependencies": {
|
|
10
|
-
"
|
|
12
|
+
"po6-socket": "^0.0.1",
|
|
13
|
+
"ya-struct": "^0.0.4"
|
|
11
14
|
},
|
|
12
15
|
"devDependencies": {
|
|
13
|
-
"
|
|
16
|
+
"c8": "^7.10.0",
|
|
17
|
+
"eslint": "^7.32.0",
|
|
18
|
+
"mocha": "^9.1.3"
|
|
14
19
|
},
|
|
15
20
|
"repository": {
|
|
16
21
|
"type": "git",
|
|
17
22
|
"url": "git+https://github.com/k13-engineering/node-netlink.git"
|
|
18
23
|
},
|
|
19
24
|
"author": "Simon Kadisch",
|
|
20
|
-
"license": "
|
|
25
|
+
"license": "LGPL 2.1",
|
|
21
26
|
"bugs": {
|
|
22
27
|
"url": "https://github.com/k13-engineering/node-netlink/issues"
|
|
23
28
|
},
|
package/samples/test.js
ADDED
package/test/base.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/* global describe */
|
|
2
|
+
/* global it */
|
|
3
|
+
|
|
4
|
+
import netlink from "../lib/index.js";
|
|
2
5
|
|
|
3
6
|
const NETLINK_ROUTE = 0;
|
|
4
7
|
|
|
@@ -7,10 +10,11 @@ describe("opening", function () {
|
|
|
7
10
|
|
|
8
11
|
describe("normal case", () => {
|
|
9
12
|
it("should open without error", () => {
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
netlink.open({
|
|
14
|
+
family: NETLINK_ROUTE
|
|
15
|
+
}).then((nl) => {
|
|
16
|
+
return nl.close();
|
|
12
17
|
});
|
|
13
|
-
nl.close();
|
|
14
18
|
});
|
|
15
19
|
});
|
|
16
20
|
});
|
package/.npmignore
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# Logs
|
|
2
|
-
logs
|
|
3
|
-
*.log
|
|
4
|
-
npm-debug.log*
|
|
5
|
-
yarn-debug.log*
|
|
6
|
-
yarn-error.log*
|
|
7
|
-
|
|
8
|
-
# Runtime data
|
|
9
|
-
pids
|
|
10
|
-
*.pid
|
|
11
|
-
*.seed
|
|
12
|
-
*.pid.lock
|
|
13
|
-
|
|
14
|
-
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
15
|
-
lib-cov
|
|
16
|
-
|
|
17
|
-
# Coverage directory used by tools like istanbul
|
|
18
|
-
coverage
|
|
19
|
-
|
|
20
|
-
# nyc test coverage
|
|
21
|
-
.nyc_output
|
|
22
|
-
|
|
23
|
-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
24
|
-
.grunt
|
|
25
|
-
|
|
26
|
-
# Bower dependency directory (https://bower.io/)
|
|
27
|
-
bower_components
|
|
28
|
-
|
|
29
|
-
# node-waf configuration
|
|
30
|
-
.lock-wscript
|
|
31
|
-
|
|
32
|
-
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
33
|
-
build/Release
|
|
34
|
-
|
|
35
|
-
# Dependency directories
|
|
36
|
-
node_modules/
|
|
37
|
-
jspm_packages/
|
|
38
|
-
|
|
39
|
-
# Typescript v1 declaration files
|
|
40
|
-
typings/
|
|
41
|
-
|
|
42
|
-
# Optional npm cache directory
|
|
43
|
-
.npm
|
|
44
|
-
|
|
45
|
-
# Optional eslint cache
|
|
46
|
-
.eslintcache
|
|
47
|
-
|
|
48
|
-
# Optional REPL history
|
|
49
|
-
.node_repl_history
|
|
50
|
-
|
|
51
|
-
# Output of 'npm pack'
|
|
52
|
-
*.tgz
|
|
53
|
-
|
|
54
|
-
# Yarn Integrity file
|
|
55
|
-
.yarn-integrity
|
|
56
|
-
|
|
57
|
-
# dotenv environment variables file
|
|
58
|
-
.env
|
|
59
|
-
|
|
60
|
-
build/
|
|
61
|
-
package-lock.json
|
package/binding.gyp
DELETED
package/index.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
const assert = require("assert");
|
|
2
|
-
const fs = require("fs");
|
|
3
|
-
const EventEmitter = require("events");
|
|
4
|
-
|
|
5
|
-
const native = require("./build/Release/netlink-native.node");
|
|
6
|
-
|
|
7
|
-
const AF_NETLINK = 16;
|
|
8
|
-
const SOCK_DGRAM = 2;
|
|
9
|
-
|
|
10
|
-
const NLM_F_ECHO = 8;
|
|
11
|
-
const NLM_F_REQUEST = 1;
|
|
12
|
-
const NLM_F_MULTI = 2;
|
|
13
|
-
const NLM_F_DUMP = 0x300;
|
|
14
|
-
const NLM_F_ACK = 4;
|
|
15
|
-
const NLM_F_CREATE = 0x400;
|
|
16
|
-
const NLM_F_EXCL = 0x200;
|
|
17
|
-
|
|
18
|
-
const NLMSG_ERROR = 2;
|
|
19
|
-
const NLMSG_DONE = 3;
|
|
20
|
-
|
|
21
|
-
const open = (opts) => {
|
|
22
|
-
const family = opts.family;
|
|
23
|
-
assert(typeof family === "number" && !isNaN(family) && family >= 0, "opts.family should be a positive integer");
|
|
24
|
-
|
|
25
|
-
const emitter = new EventEmitter();
|
|
26
|
-
|
|
27
|
-
const n = new native.NetlinkSocket(opts.family, (msg) => {
|
|
28
|
-
emitter.emit("message", msg);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
let requested = {};
|
|
32
|
-
|
|
33
|
-
emitter.on("message", (msg) => {
|
|
34
|
-
// console.log("msg =", msg);
|
|
35
|
-
|
|
36
|
-
msg.forEach((part) => {
|
|
37
|
-
if (!(part.header.nlmsg_flags & NLM_F_REQUEST)) {
|
|
38
|
-
if (part.header.nlmsg_seq > 0) {
|
|
39
|
-
const req = requested[part.header.nlmsg_seq];
|
|
40
|
-
|
|
41
|
-
if (req) {
|
|
42
|
-
req.multi |= part.header.nlmsg_flags & NLM_F_MULTI;
|
|
43
|
-
|
|
44
|
-
if (part.header.nlmsg_type === NLMSG_ERROR) {
|
|
45
|
-
const err = part.payload.readInt32LE(0);
|
|
46
|
-
|
|
47
|
-
if (err === 0) {
|
|
48
|
-
req.cb(null, []);
|
|
49
|
-
} else {
|
|
50
|
-
req.cb(err);
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
if (req.multi) {
|
|
54
|
-
req.msgs = req.msgs.concat([part]);
|
|
55
|
-
|
|
56
|
-
if (part.header.nlmsg_type === NLMSG_DONE) {
|
|
57
|
-
req.cb(null, req.msgs);
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
req.cb(null, [part]);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
console.warn("could not find pending request for seq=" + part.header.nlmsg_seq);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
let seq = 1;
|
|
72
|
-
|
|
73
|
-
const send = (obj) => new Promise((resolve, reject) => {
|
|
74
|
-
assert(typeof obj.header === "object" &&
|
|
75
|
-
!Buffer.isBuffer(obj.header) &&
|
|
76
|
-
!Array.isArray(obj.header), "header must be an object");
|
|
77
|
-
|
|
78
|
-
let header = Object.assign({}, obj.header);
|
|
79
|
-
|
|
80
|
-
if (header.nlmsg_seq === undefined) {
|
|
81
|
-
header = Object.assign({}, header, {
|
|
82
|
-
"nlmsg_seq": seq
|
|
83
|
-
});
|
|
84
|
-
seq += 1;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
n.send(header, obj.payload, obj.flags, (err) => {
|
|
88
|
-
resolve();
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const talk = (obj) => {
|
|
93
|
-
return new Promise((resolve, reject) => {
|
|
94
|
-
const header = Object.assign({}, obj.header, {
|
|
95
|
-
"nlmsg_seq": seq
|
|
96
|
-
});
|
|
97
|
-
seq += 1;
|
|
98
|
-
|
|
99
|
-
const schedule = setTimeout(() => {
|
|
100
|
-
Reflect.deleteProperty(requested, header.nlmsg_seq);
|
|
101
|
-
reject(new Error("timeout reached"));
|
|
102
|
-
}, obj.timeout || 10000);
|
|
103
|
-
|
|
104
|
-
requested[header.nlmsg_seq] = {
|
|
105
|
-
"msgs": [],
|
|
106
|
-
"multi": false,
|
|
107
|
-
"schedule": schedule,
|
|
108
|
-
"cb": (err, data) => {
|
|
109
|
-
clearTimeout(schedule);
|
|
110
|
-
Reflect.deleteProperty(requested, header.nlmsg_seq);
|
|
111
|
-
|
|
112
|
-
if (err) {
|
|
113
|
-
reject(err);
|
|
114
|
-
} else {
|
|
115
|
-
resolve(data);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
send({
|
|
121
|
-
"header": header,
|
|
122
|
-
"payload": obj.payload
|
|
123
|
-
}).catch(reject);
|
|
124
|
-
});
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return Object.freeze({
|
|
129
|
-
send,
|
|
130
|
-
talk,
|
|
131
|
-
"on": emitter.on.bind(emitter),
|
|
132
|
-
"once": emitter.once.bind(emitter),
|
|
133
|
-
"close": () => n.close()
|
|
134
|
-
});
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
module.exports = {
|
|
138
|
-
NLM_F_ECHO,
|
|
139
|
-
NLM_F_REQUEST,
|
|
140
|
-
NLM_F_MULTI,
|
|
141
|
-
NLM_F_DUMP,
|
|
142
|
-
NLM_F_ACK,
|
|
143
|
-
NLM_F_CREATE,
|
|
144
|
-
NLM_F_EXCL,
|
|
145
|
-
|
|
146
|
-
NLMSG_DONE,
|
|
147
|
-
NLMSG_ERROR,
|
|
148
|
-
|
|
149
|
-
open
|
|
150
|
-
};
|