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.
@@ -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,8 @@
1
+ [workspace]
2
+ members = ["smolvm-core"]
3
+ resolver = "2"
4
+
5
+ [profile.release]
6
+ opt-level = 3
7
+ lto = "fat"
8
+ codegen-units = 1
@@ -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
+ }