smolvm-core 0.0.9__tar.gz
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.
- smolvm_core-0.0.9/Cargo.lock +550 -0
- smolvm_core-0.0.9/Cargo.toml +8 -0
- smolvm_core-0.0.9/PKG-INFO +38 -0
- smolvm_core-0.0.9/README.md +13 -0
- smolvm_core-0.0.9/pyproject.toml +38 -0
- smolvm_core-0.0.9/python/smolvm_core/__init__.py +34 -0
- smolvm_core-0.0.9/smolvm-core/Cargo.toml +27 -0
- smolvm_core-0.0.9/smolvm-core/README.md +13 -0
- smolvm_core-0.0.9/smolvm-core/src/error.rs +45 -0
- smolvm_core-0.0.9/smolvm-core/src/lib.rs +127 -0
- smolvm_core-0.0.9/smolvm-core/src/route.rs +195 -0
- smolvm_core-0.0.9/smolvm-core/src/sysctl.rs +23 -0
- smolvm_core-0.0.9/smolvm-core/src/tap.rs +91 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "anyhow"
|
|
7
|
+
version = "1.0.102"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "arc-swap"
|
|
13
|
+
version = "1.9.1"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"rustversion",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[[package]]
|
|
21
|
+
name = "autocfg"
|
|
22
|
+
version = "1.5.0"
|
|
23
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
24
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "bitflags"
|
|
28
|
+
version = "2.11.0"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "byteorder"
|
|
34
|
+
version = "1.5.0"
|
|
35
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
36
|
+
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "bytes"
|
|
40
|
+
version = "1.11.1"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "cfg-if"
|
|
46
|
+
version = "1.0.4"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|
49
|
+
|
|
50
|
+
[[package]]
|
|
51
|
+
name = "cfg_aliases"
|
|
52
|
+
version = "0.2.1"
|
|
53
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
54
|
+
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
|
55
|
+
|
|
56
|
+
[[package]]
|
|
57
|
+
name = "futures"
|
|
58
|
+
version = "0.3.32"
|
|
59
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
60
|
+
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
|
61
|
+
dependencies = [
|
|
62
|
+
"futures-channel",
|
|
63
|
+
"futures-core",
|
|
64
|
+
"futures-executor",
|
|
65
|
+
"futures-io",
|
|
66
|
+
"futures-sink",
|
|
67
|
+
"futures-task",
|
|
68
|
+
"futures-util",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[[package]]
|
|
72
|
+
name = "futures-channel"
|
|
73
|
+
version = "0.3.32"
|
|
74
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
75
|
+
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
|
76
|
+
dependencies = [
|
|
77
|
+
"futures-core",
|
|
78
|
+
"futures-sink",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
[[package]]
|
|
82
|
+
name = "futures-core"
|
|
83
|
+
version = "0.3.32"
|
|
84
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
85
|
+
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
|
86
|
+
|
|
87
|
+
[[package]]
|
|
88
|
+
name = "futures-executor"
|
|
89
|
+
version = "0.3.32"
|
|
90
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
91
|
+
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
|
|
92
|
+
dependencies = [
|
|
93
|
+
"futures-core",
|
|
94
|
+
"futures-task",
|
|
95
|
+
"futures-util",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
[[package]]
|
|
99
|
+
name = "futures-io"
|
|
100
|
+
version = "0.3.32"
|
|
101
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
102
|
+
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
|
103
|
+
|
|
104
|
+
[[package]]
|
|
105
|
+
name = "futures-macro"
|
|
106
|
+
version = "0.3.32"
|
|
107
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
108
|
+
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
|
109
|
+
dependencies = [
|
|
110
|
+
"proc-macro2",
|
|
111
|
+
"quote",
|
|
112
|
+
"syn",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[[package]]
|
|
116
|
+
name = "futures-sink"
|
|
117
|
+
version = "0.3.32"
|
|
118
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
119
|
+
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "futures-task"
|
|
123
|
+
version = "0.3.32"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "futures-util"
|
|
129
|
+
version = "0.3.32"
|
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
+
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
|
132
|
+
dependencies = [
|
|
133
|
+
"futures-channel",
|
|
134
|
+
"futures-core",
|
|
135
|
+
"futures-io",
|
|
136
|
+
"futures-macro",
|
|
137
|
+
"futures-sink",
|
|
138
|
+
"futures-task",
|
|
139
|
+
"memchr",
|
|
140
|
+
"pin-project-lite",
|
|
141
|
+
"slab",
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
[[package]]
|
|
145
|
+
name = "heck"
|
|
146
|
+
version = "0.5.0"
|
|
147
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
148
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
149
|
+
|
|
150
|
+
[[package]]
|
|
151
|
+
name = "libc"
|
|
152
|
+
version = "0.2.184"
|
|
153
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
154
|
+
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
|
|
155
|
+
|
|
156
|
+
[[package]]
|
|
157
|
+
name = "log"
|
|
158
|
+
version = "0.4.29"
|
|
159
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
160
|
+
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
161
|
+
|
|
162
|
+
[[package]]
|
|
163
|
+
name = "memchr"
|
|
164
|
+
version = "2.8.0"
|
|
165
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
166
|
+
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
167
|
+
|
|
168
|
+
[[package]]
|
|
169
|
+
name = "memoffset"
|
|
170
|
+
version = "0.9.1"
|
|
171
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
172
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
173
|
+
dependencies = [
|
|
174
|
+
"autocfg",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
[[package]]
|
|
178
|
+
name = "mio"
|
|
179
|
+
version = "1.2.0"
|
|
180
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
181
|
+
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
|
182
|
+
dependencies = [
|
|
183
|
+
"libc",
|
|
184
|
+
"wasi",
|
|
185
|
+
"windows-sys",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[[package]]
|
|
189
|
+
name = "netlink-packet-core"
|
|
190
|
+
version = "0.7.0"
|
|
191
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
192
|
+
checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4"
|
|
193
|
+
dependencies = [
|
|
194
|
+
"anyhow",
|
|
195
|
+
"byteorder",
|
|
196
|
+
"netlink-packet-utils",
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[[package]]
|
|
200
|
+
name = "netlink-packet-route"
|
|
201
|
+
version = "0.19.0"
|
|
202
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
203
|
+
checksum = "74c171cd77b4ee8c7708da746ce392440cb7bcf618d122ec9ecc607b12938bf4"
|
|
204
|
+
dependencies = [
|
|
205
|
+
"anyhow",
|
|
206
|
+
"byteorder",
|
|
207
|
+
"libc",
|
|
208
|
+
"log",
|
|
209
|
+
"netlink-packet-core",
|
|
210
|
+
"netlink-packet-utils",
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
[[package]]
|
|
214
|
+
name = "netlink-packet-utils"
|
|
215
|
+
version = "0.5.2"
|
|
216
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
217
|
+
checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34"
|
|
218
|
+
dependencies = [
|
|
219
|
+
"anyhow",
|
|
220
|
+
"byteorder",
|
|
221
|
+
"paste",
|
|
222
|
+
"thiserror 1.0.69",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
[[package]]
|
|
226
|
+
name = "netlink-proto"
|
|
227
|
+
version = "0.11.5"
|
|
228
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
229
|
+
checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60"
|
|
230
|
+
dependencies = [
|
|
231
|
+
"bytes",
|
|
232
|
+
"futures",
|
|
233
|
+
"log",
|
|
234
|
+
"netlink-packet-core",
|
|
235
|
+
"netlink-sys",
|
|
236
|
+
"thiserror 2.0.18",
|
|
237
|
+
]
|
|
238
|
+
|
|
239
|
+
[[package]]
|
|
240
|
+
name = "netlink-sys"
|
|
241
|
+
version = "0.8.8"
|
|
242
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
243
|
+
checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae"
|
|
244
|
+
dependencies = [
|
|
245
|
+
"bytes",
|
|
246
|
+
"futures-util",
|
|
247
|
+
"libc",
|
|
248
|
+
"log",
|
|
249
|
+
"tokio",
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
[[package]]
|
|
253
|
+
name = "nix"
|
|
254
|
+
version = "0.27.1"
|
|
255
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
256
|
+
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
|
257
|
+
dependencies = [
|
|
258
|
+
"bitflags",
|
|
259
|
+
"cfg-if",
|
|
260
|
+
"libc",
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
[[package]]
|
|
264
|
+
name = "nix"
|
|
265
|
+
version = "0.29.0"
|
|
266
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
267
|
+
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
|
268
|
+
dependencies = [
|
|
269
|
+
"bitflags",
|
|
270
|
+
"cfg-if",
|
|
271
|
+
"cfg_aliases",
|
|
272
|
+
"libc",
|
|
273
|
+
"memoffset",
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
[[package]]
|
|
277
|
+
name = "once_cell"
|
|
278
|
+
version = "1.21.4"
|
|
279
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
280
|
+
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|
281
|
+
|
|
282
|
+
[[package]]
|
|
283
|
+
name = "paste"
|
|
284
|
+
version = "1.0.15"
|
|
285
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
286
|
+
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
287
|
+
|
|
288
|
+
[[package]]
|
|
289
|
+
name = "pin-project-lite"
|
|
290
|
+
version = "0.2.17"
|
|
291
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
292
|
+
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
|
293
|
+
|
|
294
|
+
[[package]]
|
|
295
|
+
name = "portable-atomic"
|
|
296
|
+
version = "1.13.1"
|
|
297
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
298
|
+
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
|
299
|
+
|
|
300
|
+
[[package]]
|
|
301
|
+
name = "proc-macro2"
|
|
302
|
+
version = "1.0.106"
|
|
303
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
304
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
305
|
+
dependencies = [
|
|
306
|
+
"unicode-ident",
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
[[package]]
|
|
310
|
+
name = "pyo3"
|
|
311
|
+
version = "0.28.3"
|
|
312
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
313
|
+
checksum = "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
|
|
314
|
+
dependencies = [
|
|
315
|
+
"libc",
|
|
316
|
+
"once_cell",
|
|
317
|
+
"portable-atomic",
|
|
318
|
+
"pyo3-build-config",
|
|
319
|
+
"pyo3-ffi",
|
|
320
|
+
"pyo3-macros",
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
[[package]]
|
|
324
|
+
name = "pyo3-build-config"
|
|
325
|
+
version = "0.28.3"
|
|
326
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
327
|
+
checksum = "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
|
|
328
|
+
dependencies = [
|
|
329
|
+
"target-lexicon",
|
|
330
|
+
]
|
|
331
|
+
|
|
332
|
+
[[package]]
|
|
333
|
+
name = "pyo3-ffi"
|
|
334
|
+
version = "0.28.3"
|
|
335
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
336
|
+
checksum = "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
|
|
337
|
+
dependencies = [
|
|
338
|
+
"libc",
|
|
339
|
+
"pyo3-build-config",
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
[[package]]
|
|
343
|
+
name = "pyo3-log"
|
|
344
|
+
version = "0.13.3"
|
|
345
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
346
|
+
checksum = "26c2ec80932c5c3b2d4fbc578c9b56b2d4502098587edb8bef5b6bfcad43682e"
|
|
347
|
+
dependencies = [
|
|
348
|
+
"arc-swap",
|
|
349
|
+
"log",
|
|
350
|
+
"pyo3",
|
|
351
|
+
]
|
|
352
|
+
|
|
353
|
+
[[package]]
|
|
354
|
+
name = "pyo3-macros"
|
|
355
|
+
version = "0.28.3"
|
|
356
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
357
|
+
checksum = "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
|
|
358
|
+
dependencies = [
|
|
359
|
+
"proc-macro2",
|
|
360
|
+
"pyo3-macros-backend",
|
|
361
|
+
"quote",
|
|
362
|
+
"syn",
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
[[package]]
|
|
366
|
+
name = "pyo3-macros-backend"
|
|
367
|
+
version = "0.28.3"
|
|
368
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
369
|
+
checksum = "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
|
|
370
|
+
dependencies = [
|
|
371
|
+
"heck",
|
|
372
|
+
"proc-macro2",
|
|
373
|
+
"pyo3-build-config",
|
|
374
|
+
"quote",
|
|
375
|
+
"syn",
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
[[package]]
|
|
379
|
+
name = "quote"
|
|
380
|
+
version = "1.0.45"
|
|
381
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
382
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
383
|
+
dependencies = [
|
|
384
|
+
"proc-macro2",
|
|
385
|
+
]
|
|
386
|
+
|
|
387
|
+
[[package]]
|
|
388
|
+
name = "rtnetlink"
|
|
389
|
+
version = "0.14.1"
|
|
390
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
391
|
+
checksum = "b684475344d8df1859ddb2d395dd3dac4f8f3422a1aa0725993cb375fc5caba5"
|
|
392
|
+
dependencies = [
|
|
393
|
+
"futures",
|
|
394
|
+
"log",
|
|
395
|
+
"netlink-packet-core",
|
|
396
|
+
"netlink-packet-route",
|
|
397
|
+
"netlink-packet-utils",
|
|
398
|
+
"netlink-proto",
|
|
399
|
+
"netlink-sys",
|
|
400
|
+
"nix 0.27.1",
|
|
401
|
+
"thiserror 1.0.69",
|
|
402
|
+
"tokio",
|
|
403
|
+
]
|
|
404
|
+
|
|
405
|
+
[[package]]
|
|
406
|
+
name = "rustversion"
|
|
407
|
+
version = "1.0.22"
|
|
408
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
409
|
+
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
410
|
+
|
|
411
|
+
[[package]]
|
|
412
|
+
name = "slab"
|
|
413
|
+
version = "0.4.12"
|
|
414
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
415
|
+
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
|
416
|
+
|
|
417
|
+
[[package]]
|
|
418
|
+
name = "smolvm-core"
|
|
419
|
+
version = "0.0.9"
|
|
420
|
+
dependencies = [
|
|
421
|
+
"futures-util",
|
|
422
|
+
"libc",
|
|
423
|
+
"log",
|
|
424
|
+
"netlink-packet-route",
|
|
425
|
+
"nix 0.29.0",
|
|
426
|
+
"pyo3",
|
|
427
|
+
"pyo3-log",
|
|
428
|
+
"rtnetlink",
|
|
429
|
+
"thiserror 1.0.69",
|
|
430
|
+
"tokio",
|
|
431
|
+
]
|
|
432
|
+
|
|
433
|
+
[[package]]
|
|
434
|
+
name = "socket2"
|
|
435
|
+
version = "0.6.3"
|
|
436
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
437
|
+
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
|
438
|
+
dependencies = [
|
|
439
|
+
"libc",
|
|
440
|
+
"windows-sys",
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
[[package]]
|
|
444
|
+
name = "syn"
|
|
445
|
+
version = "2.0.117"
|
|
446
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
447
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
448
|
+
dependencies = [
|
|
449
|
+
"proc-macro2",
|
|
450
|
+
"quote",
|
|
451
|
+
"unicode-ident",
|
|
452
|
+
]
|
|
453
|
+
|
|
454
|
+
[[package]]
|
|
455
|
+
name = "target-lexicon"
|
|
456
|
+
version = "0.13.5"
|
|
457
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
458
|
+
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
459
|
+
|
|
460
|
+
[[package]]
|
|
461
|
+
name = "thiserror"
|
|
462
|
+
version = "1.0.69"
|
|
463
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
464
|
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
465
|
+
dependencies = [
|
|
466
|
+
"thiserror-impl 1.0.69",
|
|
467
|
+
]
|
|
468
|
+
|
|
469
|
+
[[package]]
|
|
470
|
+
name = "thiserror"
|
|
471
|
+
version = "2.0.18"
|
|
472
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
473
|
+
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
|
474
|
+
dependencies = [
|
|
475
|
+
"thiserror-impl 2.0.18",
|
|
476
|
+
]
|
|
477
|
+
|
|
478
|
+
[[package]]
|
|
479
|
+
name = "thiserror-impl"
|
|
480
|
+
version = "1.0.69"
|
|
481
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
482
|
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
483
|
+
dependencies = [
|
|
484
|
+
"proc-macro2",
|
|
485
|
+
"quote",
|
|
486
|
+
"syn",
|
|
487
|
+
]
|
|
488
|
+
|
|
489
|
+
[[package]]
|
|
490
|
+
name = "thiserror-impl"
|
|
491
|
+
version = "2.0.18"
|
|
492
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
493
|
+
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
|
494
|
+
dependencies = [
|
|
495
|
+
"proc-macro2",
|
|
496
|
+
"quote",
|
|
497
|
+
"syn",
|
|
498
|
+
]
|
|
499
|
+
|
|
500
|
+
[[package]]
|
|
501
|
+
name = "tokio"
|
|
502
|
+
version = "1.51.1"
|
|
503
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
504
|
+
checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c"
|
|
505
|
+
dependencies = [
|
|
506
|
+
"libc",
|
|
507
|
+
"mio",
|
|
508
|
+
"pin-project-lite",
|
|
509
|
+
"socket2",
|
|
510
|
+
"tokio-macros",
|
|
511
|
+
"windows-sys",
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
[[package]]
|
|
515
|
+
name = "tokio-macros"
|
|
516
|
+
version = "2.7.0"
|
|
517
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
518
|
+
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
|
519
|
+
dependencies = [
|
|
520
|
+
"proc-macro2",
|
|
521
|
+
"quote",
|
|
522
|
+
"syn",
|
|
523
|
+
]
|
|
524
|
+
|
|
525
|
+
[[package]]
|
|
526
|
+
name = "unicode-ident"
|
|
527
|
+
version = "1.0.24"
|
|
528
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
529
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
530
|
+
|
|
531
|
+
[[package]]
|
|
532
|
+
name = "wasi"
|
|
533
|
+
version = "0.11.1+wasi-snapshot-preview1"
|
|
534
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
535
|
+
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
|
536
|
+
|
|
537
|
+
[[package]]
|
|
538
|
+
name = "windows-link"
|
|
539
|
+
version = "0.2.1"
|
|
540
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
541
|
+
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
542
|
+
|
|
543
|
+
[[package]]
|
|
544
|
+
name = "windows-sys"
|
|
545
|
+
version = "0.61.2"
|
|
546
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
547
|
+
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|
548
|
+
dependencies = [
|
|
549
|
+
"windows-link",
|
|
550
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smolvm-core
|
|
3
|
+
Version: 0.0.9
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
7
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
8
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Rust
|
|
15
|
+
Summary: Rust-accelerated network operations for SmolVM
|
|
16
|
+
Home-Page: https://github.com/CelestoAI/SmolVM
|
|
17
|
+
Author-email: Celesto AI <hello@celesto.ai>
|
|
18
|
+
License-Expression: Apache-2.0
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
21
|
+
Project-URL: Documentation, https://docs.celesto.ai
|
|
22
|
+
Project-URL: Homepage, https://github.com/CelestoAI/SmolVM
|
|
23
|
+
Project-URL: Repository, https://github.com/CelestoAI/SmolVM
|
|
24
|
+
|
|
25
|
+
# smolvm-core
|
|
26
|
+
|
|
27
|
+
`smolvm-core` is the small native helper package for SmolVM. It handles low-level system work such as fast network setup, while the main `smolvm` package keeps the public Python API.
|
|
28
|
+
|
|
29
|
+
Most users should install `smolvm`, not `smolvm-core` directly:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install smolvm
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That install pulls in the matching `smolvm-core` wheel automatically on supported platforms.
|
|
36
|
+
|
|
37
|
+
Install `smolvm-core` directly only if you are developing the native extension or testing package releases.
|
|
38
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# smolvm-core
|
|
2
|
+
|
|
3
|
+
`smolvm-core` is the small native helper package for SmolVM. It handles low-level system work such as fast network setup, while the main `smolvm` package keeps the public Python API.
|
|
4
|
+
|
|
5
|
+
Most users should install `smolvm`, not `smolvm-core` directly:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install smolvm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That install pulls in the matching `smolvm-core` wheel automatically on supported platforms.
|
|
12
|
+
|
|
13
|
+
Install `smolvm-core` directly only if you are developing the native extension or testing package releases.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1.7,<2.0"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "smolvm-core"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Rust-accelerated network operations for SmolVM"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Celesto AI", email = "hello@celesto.ai" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: Apache Software License",
|
|
19
|
+
"Operating System :: POSIX :: Linux",
|
|
20
|
+
"Operating System :: MacOS :: MacOS X",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Programming Language :: Rust",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/CelestoAI/SmolVM"
|
|
31
|
+
Repository = "https://github.com/CelestoAI/SmolVM"
|
|
32
|
+
Documentation = "https://docs.celesto.ai"
|
|
33
|
+
|
|
34
|
+
[tool.maturin]
|
|
35
|
+
features = ["pyo3/extension-module"]
|
|
36
|
+
module-name = "smolvm_core._smolvm_core"
|
|
37
|
+
manifest-path = "smolvm-core/Cargo.toml"
|
|
38
|
+
python-source = "python"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""SmolVM Core — Rust-accelerated network operations.
|
|
2
|
+
|
|
3
|
+
This package provides fast TAP device, route, and sysctl operations
|
|
4
|
+
via direct kernel netlink API calls, replacing subprocess calls to
|
|
5
|
+
ip, nft, and sysctl.
|
|
6
|
+
|
|
7
|
+
On Linux, all operations use netlink (zero subprocess overhead).
|
|
8
|
+
On macOS, functions are available but raise OSError (use smolvm's
|
|
9
|
+
subprocess fallback instead).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from smolvm_core._smolvm_core import (
|
|
13
|
+
add_addr,
|
|
14
|
+
add_route,
|
|
15
|
+
create_tap,
|
|
16
|
+
delete_tap,
|
|
17
|
+
flush_addrs,
|
|
18
|
+
get_default_interface,
|
|
19
|
+
is_available,
|
|
20
|
+
set_link_up,
|
|
21
|
+
write_sysctl,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"is_available",
|
|
26
|
+
"create_tap",
|
|
27
|
+
"delete_tap",
|
|
28
|
+
"set_link_up",
|
|
29
|
+
"flush_addrs",
|
|
30
|
+
"add_addr",
|
|
31
|
+
"add_route",
|
|
32
|
+
"get_default_interface",
|
|
33
|
+
"write_sysctl",
|
|
34
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "smolvm-core"
|
|
3
|
+
version = "0.0.9"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
license = "Apache-2.0"
|
|
6
|
+
description = "Rust-native core package for SmolVM"
|
|
7
|
+
homepage = "https://github.com/CelestoAI/SmolVM"
|
|
8
|
+
repository = "https://github.com/CelestoAI/SmolVM"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
|
|
11
|
+
[lib]
|
|
12
|
+
name = "_smolvm_core"
|
|
13
|
+
crate-type = ["cdylib", "rlib"]
|
|
14
|
+
|
|
15
|
+
[dependencies]
|
|
16
|
+
pyo3 = { version = "0.28", features = ["extension-module"] }
|
|
17
|
+
libc = "0.2"
|
|
18
|
+
thiserror = "1"
|
|
19
|
+
pyo3-log = "0.13"
|
|
20
|
+
log = "0.4"
|
|
21
|
+
|
|
22
|
+
[target.'cfg(target_os = "linux")'.dependencies]
|
|
23
|
+
nix = { version = "0.29", features = ["ioctl", "net", "user"] }
|
|
24
|
+
rtnetlink = "0.14"
|
|
25
|
+
netlink-packet-route = "0.19"
|
|
26
|
+
tokio = { version = "1", features = ["rt", "net", "macros"] }
|
|
27
|
+
futures-util = "0.3"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# smolvm-core
|
|
2
|
+
|
|
3
|
+
`smolvm-core` is the small native helper package for SmolVM. It handles low-level system work such as fast network setup, while the main `smolvm` package keeps the public Python API.
|
|
4
|
+
|
|
5
|
+
Most users should install `smolvm`, not `smolvm-core` directly:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install smolvm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That install pulls in the matching `smolvm-core` wheel automatically on supported platforms.
|
|
12
|
+
|
|
13
|
+
Install `smolvm-core` directly only if you are developing the native extension or testing package releases.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//! Error types that map to Python exceptions with kernel-compatible messages.
|
|
2
|
+
|
|
3
|
+
use pyo3::exceptions::PyOSError;
|
|
4
|
+
use pyo3::PyErr;
|
|
5
|
+
|
|
6
|
+
#[derive(Debug, thiserror::Error)]
|
|
7
|
+
pub enum NetlinkError {
|
|
8
|
+
#[error("File exists")]
|
|
9
|
+
AlreadyExists,
|
|
10
|
+
|
|
11
|
+
#[error("Device or resource busy")]
|
|
12
|
+
DeviceBusy,
|
|
13
|
+
|
|
14
|
+
#[error("Cannot find device \"{0}\"")]
|
|
15
|
+
DeviceNotFound(String),
|
|
16
|
+
|
|
17
|
+
#[error("RTNETLINK answers: File exists")]
|
|
18
|
+
RouteExists,
|
|
19
|
+
|
|
20
|
+
#[error("RTNETLINK answers: No such device")]
|
|
21
|
+
NoSuchDevice(String),
|
|
22
|
+
|
|
23
|
+
#[error("{0}")]
|
|
24
|
+
Io(#[from] std::io::Error),
|
|
25
|
+
|
|
26
|
+
#[error("{0}")]
|
|
27
|
+
Other(String),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl NetlinkError {
|
|
31
|
+
/// Create from an errno value with context.
|
|
32
|
+
pub fn from_errno(errno: i32, context: &str) -> Self {
|
|
33
|
+
match errno {
|
|
34
|
+
libc::EEXIST => NetlinkError::AlreadyExists,
|
|
35
|
+
libc::EBUSY => NetlinkError::DeviceBusy,
|
|
36
|
+
libc::ENODEV | libc::ENXIO => NetlinkError::NoSuchDevice(context.to_string()),
|
|
37
|
+
_ => NetlinkError::Other(format!("{}: errno {}", context, errno)),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/// Convert a NetlinkError to a Python exception.
|
|
43
|
+
pub fn to_py_err(e: NetlinkError) -> PyErr {
|
|
44
|
+
PyOSError::new_err(e.to_string())
|
|
45
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//! SmolVM native acceleration module.
|
|
2
|
+
//!
|
|
3
|
+
//! Provides fast network operations via direct kernel netlink API calls,
|
|
4
|
+
//! replacing subprocess calls to `ip`, `nft`, and `sysctl`.
|
|
5
|
+
//! Falls back gracefully on non-Linux platforms.
|
|
6
|
+
|
|
7
|
+
mod error;
|
|
8
|
+
#[cfg(target_os = "linux")]
|
|
9
|
+
mod route;
|
|
10
|
+
mod sysctl;
|
|
11
|
+
#[cfg(target_os = "linux")]
|
|
12
|
+
mod tap;
|
|
13
|
+
|
|
14
|
+
use pyo3::prelude::*;
|
|
15
|
+
|
|
16
|
+
/// Check if the native acceleration module is available and functional.
|
|
17
|
+
#[pyfunction]
|
|
18
|
+
fn is_available() -> bool {
|
|
19
|
+
cfg!(target_os = "linux")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[cfg(target_os = "linux")]
|
|
23
|
+
#[pyfunction]
|
|
24
|
+
fn create_tap(name: &str, owner_uid: u32) -> PyResult<()> {
|
|
25
|
+
tap::create(name, owner_uid).map_err(error::to_py_err)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[cfg(not(target_os = "linux"))]
|
|
29
|
+
#[pyfunction]
|
|
30
|
+
fn create_tap(_name: &str, _owner_uid: u32) -> PyResult<()> {
|
|
31
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[cfg(target_os = "linux")]
|
|
35
|
+
#[pyfunction]
|
|
36
|
+
fn delete_tap(name: &str) -> PyResult<()> {
|
|
37
|
+
tap::delete(name).map_err(error::to_py_err)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[cfg(not(target_os = "linux"))]
|
|
41
|
+
#[pyfunction]
|
|
42
|
+
fn delete_tap(_name: &str) -> PyResult<()> {
|
|
43
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[cfg(target_os = "linux")]
|
|
47
|
+
#[pyfunction]
|
|
48
|
+
fn set_link_up(name: &str) -> PyResult<()> {
|
|
49
|
+
route::set_link_up(name).map_err(error::to_py_err)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[cfg(not(target_os = "linux"))]
|
|
53
|
+
#[pyfunction]
|
|
54
|
+
fn set_link_up(_name: &str) -> PyResult<()> {
|
|
55
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#[cfg(target_os = "linux")]
|
|
59
|
+
#[pyfunction]
|
|
60
|
+
fn flush_addrs(name: &str) -> PyResult<()> {
|
|
61
|
+
route::flush_addrs(name).map_err(error::to_py_err)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[cfg(not(target_os = "linux"))]
|
|
65
|
+
#[pyfunction]
|
|
66
|
+
fn flush_addrs(_name: &str) -> PyResult<()> {
|
|
67
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[cfg(target_os = "linux")]
|
|
71
|
+
#[pyfunction]
|
|
72
|
+
fn add_addr(name: &str, ip: &str, prefix_len: u8) -> PyResult<()> {
|
|
73
|
+
route::add_addr(name, ip, prefix_len).map_err(error::to_py_err)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[cfg(not(target_os = "linux"))]
|
|
77
|
+
#[pyfunction]
|
|
78
|
+
fn add_addr(_name: &str, _ip: &str, _prefix_len: u8) -> PyResult<()> {
|
|
79
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[cfg(target_os = "linux")]
|
|
83
|
+
#[pyfunction]
|
|
84
|
+
fn add_route(dest: &str, prefix_len: u8, dev: &str) -> PyResult<()> {
|
|
85
|
+
route::add_route(dest, prefix_len, dev).map_err(error::to_py_err)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#[cfg(not(target_os = "linux"))]
|
|
89
|
+
#[pyfunction]
|
|
90
|
+
fn add_route(_dest: &str, _prefix_len: u8, _dev: &str) -> PyResult<()> {
|
|
91
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[cfg(target_os = "linux")]
|
|
95
|
+
#[pyfunction]
|
|
96
|
+
fn get_default_interface() -> PyResult<String> {
|
|
97
|
+
route::get_default_interface().map_err(error::to_py_err)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[cfg(not(target_os = "linux"))]
|
|
101
|
+
#[pyfunction]
|
|
102
|
+
fn get_default_interface() -> PyResult<String> {
|
|
103
|
+
Err(pyo3::exceptions::PyOSError::new_err("Not available on this platform"))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#[pyfunction]
|
|
107
|
+
fn write_sysctl(key: &str, value: &str) -> PyResult<()> {
|
|
108
|
+
sysctl::write(key, value).map_err(error::to_py_err)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Python module definition.
|
|
112
|
+
#[pymodule]
|
|
113
|
+
fn _smolvm_core(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
114
|
+
pyo3_log::init();
|
|
115
|
+
|
|
116
|
+
m.add_function(wrap_pyfunction!(is_available, m)?)?;
|
|
117
|
+
m.add_function(wrap_pyfunction!(create_tap, m)?)?;
|
|
118
|
+
m.add_function(wrap_pyfunction!(delete_tap, m)?)?;
|
|
119
|
+
m.add_function(wrap_pyfunction!(set_link_up, m)?)?;
|
|
120
|
+
m.add_function(wrap_pyfunction!(flush_addrs, m)?)?;
|
|
121
|
+
m.add_function(wrap_pyfunction!(add_addr, m)?)?;
|
|
122
|
+
m.add_function(wrap_pyfunction!(add_route, m)?)?;
|
|
123
|
+
m.add_function(wrap_pyfunction!(get_default_interface, m)?)?;
|
|
124
|
+
m.add_function(wrap_pyfunction!(write_sysctl, m)?)?;
|
|
125
|
+
|
|
126
|
+
Ok(())
|
|
127
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
//! IP address, link, and route management via rtnetlink.
|
|
2
|
+
|
|
3
|
+
use crate::error::NetlinkError;
|
|
4
|
+
use std::net::Ipv4Addr;
|
|
5
|
+
use std::sync::OnceLock;
|
|
6
|
+
use tokio::runtime::Runtime;
|
|
7
|
+
|
|
8
|
+
/// Shared tokio runtime for all netlink operations.
|
|
9
|
+
/// Using a dedicated single-threaded runtime to avoid nested runtime issues.
|
|
10
|
+
pub fn runtime() -> &'static Runtime {
|
|
11
|
+
static RT: OnceLock<Runtime> = OnceLock::new();
|
|
12
|
+
RT.get_or_init(|| {
|
|
13
|
+
tokio::runtime::Builder::new_current_thread()
|
|
14
|
+
.enable_all()
|
|
15
|
+
.build()
|
|
16
|
+
.expect("failed to create tokio runtime for netlink")
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Helper: create connection, run async block, return result.
|
|
21
|
+
pub fn with_netlink<F, T>(f: F) -> Result<T, NetlinkError>
|
|
22
|
+
where
|
|
23
|
+
F: std::future::Future<Output = Result<T, NetlinkError>>,
|
|
24
|
+
{
|
|
25
|
+
let rt = runtime();
|
|
26
|
+
rt.block_on(f)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Get the interface index for a device name.
|
|
30
|
+
async fn get_link_index(
|
|
31
|
+
handle: &rtnetlink::Handle,
|
|
32
|
+
name: &str,
|
|
33
|
+
) -> Result<u32, NetlinkError> {
|
|
34
|
+
use futures_util::TryStreamExt;
|
|
35
|
+
|
|
36
|
+
let mut links = handle.link().get().match_name(name.to_string()).execute();
|
|
37
|
+
let link = links.try_next().await.map_err(|e| {
|
|
38
|
+
NetlinkError::Other(format!("Failed to find device {}: {}", name, e))
|
|
39
|
+
})?;
|
|
40
|
+
|
|
41
|
+
link.map(|l| l.header.index)
|
|
42
|
+
.ok_or_else(|| NetlinkError::DeviceNotFound(name.to_string()))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Set a network interface to UP state.
|
|
46
|
+
pub fn set_link_up(name: &str) -> Result<(), NetlinkError> {
|
|
47
|
+
with_netlink(async {
|
|
48
|
+
let (connection, handle, _) = rtnetlink::new_connection()
|
|
49
|
+
.map_err(|e| NetlinkError::Other(format!("netlink: {}", e)))?;
|
|
50
|
+
tokio::spawn(connection);
|
|
51
|
+
|
|
52
|
+
let index = get_link_index(&handle, name).await?;
|
|
53
|
+
handle
|
|
54
|
+
.link()
|
|
55
|
+
.set(index)
|
|
56
|
+
.up()
|
|
57
|
+
.execute()
|
|
58
|
+
.await
|
|
59
|
+
.map_err(|e| NetlinkError::Other(format!("set_link_up {}: {}", name, e)))?;
|
|
60
|
+
|
|
61
|
+
log::debug!("Link {} set UP", name);
|
|
62
|
+
Ok(())
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Flush all IPv4 addresses from an interface.
|
|
67
|
+
pub fn flush_addrs(name: &str) -> Result<(), NetlinkError> {
|
|
68
|
+
with_netlink(async {
|
|
69
|
+
let (connection, handle, _) = rtnetlink::new_connection()
|
|
70
|
+
.map_err(|e| NetlinkError::Other(format!("netlink: {}", e)))?;
|
|
71
|
+
tokio::spawn(connection);
|
|
72
|
+
|
|
73
|
+
let index = get_link_index(&handle, name).await?;
|
|
74
|
+
|
|
75
|
+
use futures_util::TryStreamExt;
|
|
76
|
+
let mut addrs = handle.address().get().set_link_index_filter(index).execute();
|
|
77
|
+
while let Some(addr) = addrs.try_next().await.map_err(|e| {
|
|
78
|
+
NetlinkError::Other(format!("list addrs {}: {}", name, e))
|
|
79
|
+
})? {
|
|
80
|
+
handle
|
|
81
|
+
.address()
|
|
82
|
+
.del(addr)
|
|
83
|
+
.execute()
|
|
84
|
+
.await
|
|
85
|
+
.map_err(|e| NetlinkError::Other(format!("del addr {}: {}", name, e)))?;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
log::debug!("Flushed addresses on {}", name);
|
|
89
|
+
Ok(())
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Add an IP address to an interface.
|
|
94
|
+
pub fn add_addr(name: &str, ip: &str, prefix_len: u8) -> Result<(), NetlinkError> {
|
|
95
|
+
let addr: Ipv4Addr = ip
|
|
96
|
+
.parse()
|
|
97
|
+
.map_err(|e| NetlinkError::Other(format!("invalid IP {}: {}", ip, e)))?;
|
|
98
|
+
|
|
99
|
+
with_netlink(async {
|
|
100
|
+
let (connection, handle, _) = rtnetlink::new_connection()
|
|
101
|
+
.map_err(|e| NetlinkError::Other(format!("netlink: {}", e)))?;
|
|
102
|
+
tokio::spawn(connection);
|
|
103
|
+
|
|
104
|
+
let index = get_link_index(&handle, name).await?;
|
|
105
|
+
handle
|
|
106
|
+
.address()
|
|
107
|
+
.add(index, std::net::IpAddr::V4(addr), prefix_len)
|
|
108
|
+
.execute()
|
|
109
|
+
.await
|
|
110
|
+
.map_err(|e| {
|
|
111
|
+
let msg = format!("{}", e);
|
|
112
|
+
if msg.contains("File exists") || msg.contains("EEXIST") {
|
|
113
|
+
NetlinkError::RouteExists
|
|
114
|
+
} else {
|
|
115
|
+
NetlinkError::Other(format!("add_addr {} on {}: {}", ip, name, e))
|
|
116
|
+
}
|
|
117
|
+
})?;
|
|
118
|
+
|
|
119
|
+
log::debug!("Added {}/{} to {}", ip, prefix_len, name);
|
|
120
|
+
Ok(())
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Add a route: dest/prefix_len via device.
|
|
125
|
+
pub fn add_route(dest: &str, prefix_len: u8, dev: &str) -> Result<(), NetlinkError> {
|
|
126
|
+
let dest_addr: Ipv4Addr = dest
|
|
127
|
+
.parse()
|
|
128
|
+
.map_err(|e| NetlinkError::Other(format!("invalid dest {}: {}", dest, e)))?;
|
|
129
|
+
|
|
130
|
+
with_netlink(async {
|
|
131
|
+
let (connection, handle, _) = rtnetlink::new_connection()
|
|
132
|
+
.map_err(|e| NetlinkError::Other(format!("netlink: {}", e)))?;
|
|
133
|
+
tokio::spawn(connection);
|
|
134
|
+
|
|
135
|
+
let index = get_link_index(&handle, dev).await?;
|
|
136
|
+
handle
|
|
137
|
+
.route()
|
|
138
|
+
.add()
|
|
139
|
+
.v4()
|
|
140
|
+
.destination_prefix(dest_addr, prefix_len)
|
|
141
|
+
.output_interface(index)
|
|
142
|
+
.execute()
|
|
143
|
+
.await
|
|
144
|
+
.map_err(|e| {
|
|
145
|
+
let msg = format!("{}", e);
|
|
146
|
+
if msg.contains("File exists") || msg.contains("EEXIST") {
|
|
147
|
+
NetlinkError::RouteExists
|
|
148
|
+
} else {
|
|
149
|
+
NetlinkError::Other(format!("add_route {}/{} dev {}: {}", dest, prefix_len, dev, e))
|
|
150
|
+
}
|
|
151
|
+
})?;
|
|
152
|
+
|
|
153
|
+
log::debug!("Route {}/{} via {}", dest, prefix_len, dev);
|
|
154
|
+
Ok(())
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Get the default outbound network interface name.
|
|
159
|
+
pub fn get_default_interface() -> Result<String, NetlinkError> {
|
|
160
|
+
with_netlink(async {
|
|
161
|
+
let (connection, handle, _) = rtnetlink::new_connection()
|
|
162
|
+
.map_err(|e| NetlinkError::Other(format!("netlink: {}", e)))?;
|
|
163
|
+
tokio::spawn(connection);
|
|
164
|
+
|
|
165
|
+
use futures_util::TryStreamExt;
|
|
166
|
+
use netlink_packet_route::route::RouteAttribute;
|
|
167
|
+
|
|
168
|
+
let mut routes = handle.route().get(rtnetlink::IpVersion::V4).execute();
|
|
169
|
+
while let Some(route) = routes.try_next().await.map_err(|e| {
|
|
170
|
+
NetlinkError::Other(format!("get routes: {}", e))
|
|
171
|
+
})? {
|
|
172
|
+
// Default route has dst_len == 0
|
|
173
|
+
if route.header.destination_prefix_length == 0 {
|
|
174
|
+
for attr in &route.attributes {
|
|
175
|
+
if let RouteAttribute::Oif(idx) = attr {
|
|
176
|
+
// Resolve index to name
|
|
177
|
+
let mut links = handle.link().get().match_index(*idx).execute();
|
|
178
|
+
if let Some(link) = links.try_next().await.map_err(|e| {
|
|
179
|
+
NetlinkError::Other(format!("get link: {}", e))
|
|
180
|
+
})? {
|
|
181
|
+
use netlink_packet_route::link::LinkAttribute;
|
|
182
|
+
for attr in &link.attributes {
|
|
183
|
+
if let LinkAttribute::IfName(name) = attr {
|
|
184
|
+
return Ok(name.clone());
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
Err(NetlinkError::Other("No default route found".to_string()))
|
|
194
|
+
})
|
|
195
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//! sysctl via direct /proc/sys writes.
|
|
2
|
+
|
|
3
|
+
use crate::error::NetlinkError;
|
|
4
|
+
use std::fs;
|
|
5
|
+
use std::path::PathBuf;
|
|
6
|
+
|
|
7
|
+
/// Write a sysctl value via /proc/sys.
|
|
8
|
+
///
|
|
9
|
+
/// Key uses dot notation (e.g., "net.ipv4.ip_forward") which is
|
|
10
|
+
/// converted to path notation (/proc/sys/net/ipv4/ip_forward).
|
|
11
|
+
pub fn write(key: &str, value: &str) -> Result<(), NetlinkError> {
|
|
12
|
+
let path: PathBuf = ["/proc/sys"]
|
|
13
|
+
.iter()
|
|
14
|
+
.collect::<PathBuf>()
|
|
15
|
+
.join(key.replace('.', "/"));
|
|
16
|
+
|
|
17
|
+
fs::write(&path, value).map_err(|e| {
|
|
18
|
+
NetlinkError::Other(format!("sysctl write {} = {}: {}", key, value, e))
|
|
19
|
+
})?;
|
|
20
|
+
|
|
21
|
+
log::debug!("sysctl {} = {}", key, value);
|
|
22
|
+
Ok(())
|
|
23
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//! TAP device creation and deletion via ioctl + netlink.
|
|
2
|
+
|
|
3
|
+
use crate::error::NetlinkError;
|
|
4
|
+
use std::ffi::CString;
|
|
5
|
+
use std::os::fd::AsRawFd;
|
|
6
|
+
|
|
7
|
+
/// Create a TAP device with the given name and owner UID.
|
|
8
|
+
pub fn create(name: &str, owner_uid: u32) -> Result<(), NetlinkError> {
|
|
9
|
+
let fd = std::fs::OpenOptions::new()
|
|
10
|
+
.read(true)
|
|
11
|
+
.write(true)
|
|
12
|
+
.open("/dev/net/tun")
|
|
13
|
+
.map_err(NetlinkError::Io)?;
|
|
14
|
+
|
|
15
|
+
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
|
|
16
|
+
|
|
17
|
+
// Set device name
|
|
18
|
+
let name_bytes = name.as_bytes();
|
|
19
|
+
if name_bytes.len() >= libc::IFNAMSIZ {
|
|
20
|
+
return Err(NetlinkError::Other(format!(
|
|
21
|
+
"TAP name too long: {} (max {})",
|
|
22
|
+
name,
|
|
23
|
+
libc::IFNAMSIZ - 1
|
|
24
|
+
)));
|
|
25
|
+
}
|
|
26
|
+
unsafe {
|
|
27
|
+
std::ptr::copy_nonoverlapping(
|
|
28
|
+
name_bytes.as_ptr(),
|
|
29
|
+
ifr.ifr_name.as_mut_ptr() as *mut u8,
|
|
30
|
+
name_bytes.len(),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// IFF_TAP | IFF_NO_PI
|
|
35
|
+
ifr.ifr_ifru.ifru_flags = (libc::IFF_TAP | libc::IFF_NO_PI) as i16;
|
|
36
|
+
|
|
37
|
+
// TUNSETIFF
|
|
38
|
+
let ret = unsafe { libc::ioctl(fd.as_raw_fd(), 0x400454CA_u64, &ifr) };
|
|
39
|
+
if ret < 0 {
|
|
40
|
+
let errno = unsafe { *libc::__errno_location() };
|
|
41
|
+
return Err(NetlinkError::from_errno(errno, name));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// TUNSETOWNER
|
|
45
|
+
let ret = unsafe { libc::ioctl(fd.as_raw_fd(), 0x400454CC_u64, owner_uid as libc::c_ulong) };
|
|
46
|
+
if ret < 0 {
|
|
47
|
+
let errno = unsafe { *libc::__errno_location() };
|
|
48
|
+
return Err(NetlinkError::from_errno(errno, &format!("TUNSETOWNER {}", name)));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// TUNSETPERSIST
|
|
52
|
+
let ret = unsafe { libc::ioctl(fd.as_raw_fd(), 0x400454CB_u64, 1 as libc::c_int) };
|
|
53
|
+
if ret < 0 {
|
|
54
|
+
let errno = unsafe { *libc::__errno_location() };
|
|
55
|
+
return Err(NetlinkError::from_errno(errno, &format!("TUNSETPERSIST {}", name)));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
log::debug!("TAP {} created (owner UID {})", name, owner_uid);
|
|
59
|
+
Ok(())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Delete a TAP device via netlink.
|
|
63
|
+
pub fn delete(name: &str) -> Result<(), NetlinkError> {
|
|
64
|
+
use crate::route::{runtime, with_netlink};
|
|
65
|
+
|
|
66
|
+
with_netlink(async {
|
|
67
|
+
let (connection, handle, _) = rtnetlink::new_connection().map_err(|e| {
|
|
68
|
+
NetlinkError::Other(format!("netlink connection failed: {}", e))
|
|
69
|
+
})?;
|
|
70
|
+
tokio::spawn(connection);
|
|
71
|
+
|
|
72
|
+
// Find the link index
|
|
73
|
+
let mut links = handle.link().get().match_name(name.to_string()).execute();
|
|
74
|
+
use futures_util::TryStreamExt;
|
|
75
|
+
let link = links.try_next().await.map_err(|e| {
|
|
76
|
+
NetlinkError::Other(format!("Failed to find device {}: {}", name, e))
|
|
77
|
+
})?;
|
|
78
|
+
|
|
79
|
+
if let Some(link) = link {
|
|
80
|
+
let index = link.header.index;
|
|
81
|
+
handle.link().del(index).execute().await.map_err(|e| {
|
|
82
|
+
NetlinkError::Other(format!("Failed to delete {}: {}", name, e))
|
|
83
|
+
})?;
|
|
84
|
+
log::debug!("TAP {} deleted", name);
|
|
85
|
+
} else {
|
|
86
|
+
return Err(NetlinkError::DeviceNotFound(name.to_string()));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Ok(())
|
|
90
|
+
})
|
|
91
|
+
}
|