@@ -2,24 +2,28 @@ use std::{
2
2
collections:: HashMap ,
3
3
ffi:: CStr ,
4
4
io, mem,
5
+ net:: Ipv4Addr ,
5
6
os:: fd:: { AsRawFd as _, BorrowedFd , FromRawFd as _, OwnedFd } ,
6
7
ptr, slice,
7
8
} ;
8
9
9
10
use libc:: {
10
- getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket,
11
- AF_NETLINK , AF_UNSPEC , ETH_P_ALL , IFF_UP , IFLA_XDP , NETLINK_EXT_ACK , NETLINK_ROUTE ,
11
+ getsockname, nlattr, nlmsgerr, nlmsghdr, recv, send, setsockopt, sockaddr_nl, socket, AF_INET ,
12
+ AF_NETLINK , AF_UNSPEC , ETH_P_ALL , IFA_F_PERMANENT , IFA_LOCAL , IFF_UP , IFLA_IFNAME ,
13
+ IFLA_INFO_DATA , IFLA_INFO_KIND , IFLA_LINKINFO , IFLA_XDP , NETLINK_EXT_ACK , NETLINK_ROUTE ,
12
14
NLA_ALIGNTO , NLA_F_NESTED , NLA_TYPE_MASK , NLMSG_DONE , NLMSG_ERROR , NLM_F_ACK , NLM_F_CREATE ,
13
- NLM_F_DUMP , NLM_F_ECHO , NLM_F_EXCL , NLM_F_MULTI , NLM_F_REQUEST , RTM_DELTFILTER , RTM_GETTFILTER ,
14
- RTM_NEWQDISC , RTM_NEWTFILTER , RTM_SETLINK , SOCK_RAW , SOL_NETLINK ,
15
+ NLM_F_DUMP , NLM_F_ECHO , NLM_F_EXCL , NLM_F_MULTI , NLM_F_REQUEST , RTM_DELLINK , RTM_DELTFILTER ,
16
+ RTM_GETTFILTER , RTM_NEWADDR , RTM_NEWLINK , RTM_NEWQDISC , RTM_NEWTFILTER , RTM_SETLINK ,
17
+ RT_SCOPE_UNIVERSE , SOCK_RAW , SOL_NETLINK ,
15
18
} ;
16
19
use thiserror:: Error ;
17
20
18
21
use crate :: {
19
22
generated:: {
20
- ifinfomsg, tcmsg, IFLA_XDP_EXPECTED_FD , IFLA_XDP_FD , IFLA_XDP_FLAGS , NLMSG_ALIGNTO ,
21
- TCA_BPF_FD , TCA_BPF_FLAGS , TCA_BPF_FLAG_ACT_DIRECT , TCA_BPF_NAME , TCA_KIND , TCA_OPTIONS ,
22
- TC_H_CLSACT , TC_H_INGRESS , TC_H_MAJ_MASK , TC_H_UNSPEC , XDP_FLAGS_REPLACE ,
23
+ ifaddrmsg, ifinfomsg, tcmsg, IFLA_XDP_EXPECTED_FD , IFLA_XDP_FD , IFLA_XDP_FLAGS ,
24
+ NLMSG_ALIGNTO , TCA_BPF_FD , TCA_BPF_FLAGS , TCA_BPF_FLAG_ACT_DIRECT , TCA_BPF_NAME , TCA_KIND ,
25
+ TCA_OPTIONS , TC_H_CLSACT , TC_H_INGRESS , TC_H_MAJ_MASK , TC_H_UNSPEC , VETH_INFO_PEER ,
26
+ XDP_FLAGS_REPLACE ,
23
27
} ,
24
28
programs:: TcAttachType ,
25
29
util:: tc_handler_make,
@@ -266,6 +270,63 @@ pub(crate) unsafe fn netlink_find_filter_with_name(
266
270
Ok ( filter_info)
267
271
}
268
272
273
+ #[ doc( hidden) ]
274
+ pub unsafe fn netlink_add_veth_pair ( name1 : & CStr , name2 : & CStr ) -> Result < ( ) , io:: Error > {
275
+ let sock = NetlinkSocket :: open ( ) ?;
276
+
277
+ // Safety: Request is POD so this is safe
278
+ let mut req = mem:: zeroed :: < Request > ( ) ;
279
+
280
+ let nlmsg_len = mem:: size_of :: < nlmsghdr > ( ) + mem:: size_of :: < ifinfomsg > ( ) ;
281
+ req. header = nlmsghdr {
282
+ nlmsg_len : nlmsg_len as u32 ,
283
+ nlmsg_flags : ( NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE ) as u16 ,
284
+ nlmsg_type : RTM_NEWLINK ,
285
+ nlmsg_pid : 0 ,
286
+ nlmsg_seq : 1 ,
287
+ } ;
288
+ req. if_info . ifi_family = AF_UNSPEC as u8 ;
289
+ req. if_info . ifi_index = 0 ;
290
+ req. if_info . ifi_flags = 0 ;
291
+ req. if_info . ifi_change = 0 ;
292
+
293
+ let attrs_buf = request_attributes ( & mut req, nlmsg_len) ;
294
+
295
+ // add IFLA_IFNAME
296
+ let ifname_len = write_attr_bytes ( attrs_buf, 0 , IFLA_IFNAME , name1. to_bytes_with_nul ( ) ) ?;
297
+
298
+ // add IFLA_LINKINFO which includes IFLA_INFO_KIND and IFLA_INFO_DATA
299
+ let mut linkinfo = NestedAttrs :: new ( & mut attrs_buf[ ifname_len..] , IFLA_LINKINFO ) ;
300
+ linkinfo. write_attr_bytes ( IFLA_INFO_KIND , b"veth" ) ?;
301
+
302
+ linkinfo. write_nested_attrs ( IFLA_INFO_DATA , |info_data| {
303
+ info_data. write_nested_attrs ( VETH_INFO_PEER as u16 , |info_peer| {
304
+ // Safety: ifinfomsg is POD so this is safe
305
+ let mut peer_if_info = mem:: zeroed :: < ifinfomsg > ( ) ;
306
+ peer_if_info. ifi_family = AF_UNSPEC as u8 ;
307
+ peer_if_info. ifi_index = 0 ;
308
+ peer_if_info. ifi_flags = 0 ;
309
+ peer_if_info. ifi_change = 0 ;
310
+
311
+ info_peer. write_bytes ( bytes_of ( & peer_if_info) ) ?;
312
+
313
+ // add IFLA_IFNAME
314
+ info_peer. write_attr_bytes ( IFLA_IFNAME , name2. to_bytes_with_nul ( ) ) ?;
315
+
316
+ Ok ( ( ) )
317
+ } ) ?;
318
+ Ok ( ( ) )
319
+ } ) ?;
320
+
321
+ let linkinfo_len = linkinfo. finish ( ) ?;
322
+
323
+ req. header . nlmsg_len += align_to ( ifname_len + linkinfo_len, NLA_ALIGNTO as usize ) as u32 ;
324
+ sock. send ( & bytes_of ( & req) [ ..req. header . nlmsg_len as usize ] ) ?;
325
+ sock. recv ( ) ?;
326
+
327
+ Ok ( ( ) )
328
+ }
329
+
269
330
#[ doc( hidden) ]
270
331
pub unsafe fn netlink_set_link_up ( if_index : i32 ) -> Result < ( ) , io:: Error > {
271
332
let sock = NetlinkSocket :: open ( ) ?;
@@ -292,11 +353,100 @@ pub unsafe fn netlink_set_link_up(if_index: i32) -> Result<(), io::Error> {
292
353
Ok ( ( ) )
293
354
}
294
355
356
+ #[ doc( hidden) ]
357
+ pub unsafe fn netlink_set_link_down ( if_index : i32 ) -> Result < ( ) , io:: Error > {
358
+ let sock = NetlinkSocket :: open ( ) ?;
359
+
360
+ // Safety: Request is POD so this is safe
361
+ let mut req = mem:: zeroed :: < Request > ( ) ;
362
+
363
+ let nlmsg_len = mem:: size_of :: < nlmsghdr > ( ) + mem:: size_of :: < ifinfomsg > ( ) ;
364
+ req. header = nlmsghdr {
365
+ nlmsg_len : nlmsg_len as u32 ,
366
+ nlmsg_flags : ( NLM_F_REQUEST | NLM_F_ACK ) as u16 ,
367
+ nlmsg_type : RTM_SETLINK ,
368
+ nlmsg_pid : 0 ,
369
+ nlmsg_seq : 1 ,
370
+ } ;
371
+ req. if_info . ifi_family = AF_UNSPEC as u8 ;
372
+ req. if_info . ifi_index = if_index;
373
+ req. if_info . ifi_flags = 0 ;
374
+ req. if_info . ifi_change = IFF_UP as u32 ;
375
+
376
+ sock. send ( & bytes_of ( & req) [ ..req. header . nlmsg_len as usize ] ) ?;
377
+ sock. recv ( ) ?;
378
+
379
+ Ok ( ( ) )
380
+ }
381
+
382
+ #[ doc( hidden) ]
383
+ pub unsafe fn netlink_delete_link ( if_index : i32 ) -> Result < ( ) , io:: Error > {
384
+ let sock = NetlinkSocket :: open ( ) ?;
385
+
386
+ // Safety: Request is POD so this is safe
387
+ let mut req = mem:: zeroed :: < Request > ( ) ;
388
+
389
+ let nlmsg_len = mem:: size_of :: < nlmsghdr > ( ) + mem:: size_of :: < ifinfomsg > ( ) ;
390
+ req. header = nlmsghdr {
391
+ nlmsg_len : nlmsg_len as u32 ,
392
+ nlmsg_flags : ( NLM_F_REQUEST | NLM_F_ACK ) as u16 ,
393
+ nlmsg_type : RTM_DELLINK ,
394
+ nlmsg_pid : 0 ,
395
+ nlmsg_seq : 1 ,
396
+ } ;
397
+ req. if_info . ifi_family = AF_UNSPEC as u8 ;
398
+ req. if_info . ifi_index = if_index;
399
+ req. if_info . ifi_flags = 0 ;
400
+ req. if_info . ifi_change = 0 ;
401
+
402
+ sock. send ( & bytes_of ( & req) [ ..req. header . nlmsg_len as usize ] ) ?;
403
+ sock. recv ( ) ?;
404
+
405
+ Ok ( ( ) )
406
+ }
407
+
408
+ #[ doc( hidden) ]
409
+ pub unsafe fn netlink_add_ip_addr (
410
+ if_index : u32 ,
411
+ ipv4_addr : Ipv4Addr ,
412
+ ipv4_prefix : u8 ,
413
+ ) -> Result < ( ) , io:: Error > {
414
+ let sock = NetlinkSocket :: open ( ) ?;
415
+
416
+ // Safety: AddrRequest is POD so this is safe
417
+ let mut req = mem:: zeroed :: < AddrRequest > ( ) ;
418
+
419
+ let nlmsg_len = mem:: size_of :: < nlmsghdr > ( ) + mem:: size_of :: < ifaddrmsg > ( ) ;
420
+ req. header = nlmsghdr {
421
+ nlmsg_len : nlmsg_len as u32 ,
422
+ nlmsg_flags : ( NLM_F_REQUEST | NLM_F_ACK | NLM_F_EXCL | NLM_F_CREATE ) as u16 ,
423
+ nlmsg_type : RTM_NEWADDR ,
424
+ nlmsg_pid : 0 ,
425
+ nlmsg_seq : 1 ,
426
+ } ;
427
+ req. if_addr . ifa_family = AF_INET as u8 ;
428
+ req. if_addr . ifa_prefixlen = ipv4_prefix;
429
+ req. if_addr . ifa_flags = IFA_F_PERMANENT as u8 ;
430
+ req. if_addr . ifa_scope = RT_SCOPE_UNIVERSE ;
431
+ req. if_addr . ifa_index = if_index;
432
+
433
+ let attrs_buf = request_attributes ( & mut req, nlmsg_len) ;
434
+
435
+ // add IFA_LOCAL
436
+ let local_len = write_attr_bytes ( attrs_buf, 0 , IFA_LOCAL , & ipv4_addr. octets ( ) ) ?;
437
+
438
+ req. header . nlmsg_len += align_to ( local_len, NLA_ALIGNTO as usize ) as u32 ;
439
+ sock. send ( & bytes_of ( & req) [ ..req. header . nlmsg_len as usize ] ) ?;
440
+ sock. recv ( ) ?;
441
+
442
+ Ok ( ( ) )
443
+ }
444
+
295
445
#[ repr( C ) ]
296
446
struct Request {
297
447
header : nlmsghdr ,
298
448
if_info : ifinfomsg ,
299
- attrs : [ u8 ; 64 ] ,
449
+ attrs : [ u8 ; 128 ] ,
300
450
}
301
451
302
452
#[ repr( C ) ]
@@ -306,6 +456,13 @@ struct TcRequest {
306
456
attrs : [ u8 ; 64 ] ,
307
457
}
308
458
459
+ #[ repr( C ) ]
460
+ struct AddrRequest {
461
+ header : nlmsghdr ,
462
+ if_addr : ifaddrmsg ,
463
+ attrs : [ u8 ; 64 ] ,
464
+ }
465
+
309
466
struct NetlinkSocket {
310
467
sock : OwnedFd ,
311
468
_nl_pid : u32 ,
@@ -505,6 +662,23 @@ impl<'a> NestedAttrs<'a> {
505
662
Ok ( size)
506
663
}
507
664
665
+ fn write_bytes ( & mut self , value : & [ u8 ] ) -> Result < usize , io:: Error > {
666
+ let size = write_bytes ( self . buf , self . offset , value) ?;
667
+ self . offset += size;
668
+ Ok ( size)
669
+ }
670
+
671
+ fn write_nested_attrs < F > ( & mut self , attr_type : u16 , f : F ) -> Result < usize , io:: Error >
672
+ where
673
+ F : FnOnce ( & mut NestedAttrs < ' _ > ) -> Result < ( ) , io:: Error > ,
674
+ {
675
+ let mut nested_attrs = NestedAttrs :: new ( & mut self . buf [ self . offset ..] , attr_type) ;
676
+ f ( & mut nested_attrs) ?;
677
+ let size = nested_attrs. finish ( ) ?;
678
+ self . offset += size;
679
+ Ok ( size)
680
+ }
681
+
508
682
fn finish ( self ) -> Result < usize , io:: Error > {
509
683
let nla_len = self . offset ;
510
684
let attr = nlattr {
0 commit comments