@@ -1590,6 +1590,143 @@ pub fn test_socket<S: squeue::EntryMarker, C: cqueue::EntryMarker>(
1590
1590
Ok ( ( ) )
1591
1591
}
1592
1592
1593
+ pub fn test_socket_bind_listen < S : squeue:: EntryMarker , C : cqueue:: EntryMarker > (
1594
+ ring : & mut IoUring < S , C > ,
1595
+ test : & Test ,
1596
+ ) -> anyhow:: Result < ( ) > {
1597
+ use socket2:: { Domain , Protocol , Socket , Type } ;
1598
+
1599
+ require ! (
1600
+ test;
1601
+ test. probe. is_supported( opcode:: Socket :: CODE ) ;
1602
+ test. probe. is_supported( opcode:: Bind :: CODE ) ;
1603
+ test. probe. is_supported( opcode:: Listen :: CODE ) ;
1604
+ ) ;
1605
+
1606
+ println ! ( "test socket_bind_listen" ) ;
1607
+
1608
+ // Open a TCP socket through old-style `socket(2)` syscall.
1609
+ // This is used both as a kernel sanity check, and for comparing the returned io-uring FD.
1610
+ let plain_socket = Socket :: new ( Domain :: IPV4 , Type :: STREAM , Some ( Protocol :: TCP ) ) . unwrap ( ) ;
1611
+ let plain_fd = plain_socket. as_raw_fd ( ) ;
1612
+
1613
+ let socket_fd_op = opcode:: Socket :: new (
1614
+ Domain :: IPV4 . into ( ) ,
1615
+ Type :: STREAM . into ( ) ,
1616
+ Protocol :: TCP . into ( ) ,
1617
+ ) ;
1618
+ unsafe {
1619
+ ring. submission ( )
1620
+ . push ( & socket_fd_op. build ( ) . user_data ( 42 ) . into ( ) )
1621
+ . expect ( "queue is full" ) ;
1622
+ }
1623
+ ring. submit_and_wait ( 1 ) ?;
1624
+
1625
+ let cqes: Vec < cqueue:: Entry > = ring. completion ( ) . map ( Into :: into) . collect ( ) ;
1626
+ assert_eq ! ( cqes. len( ) , 1 ) ;
1627
+ assert_eq ! ( cqes[ 0 ] . user_data( ) , 42 ) ;
1628
+ assert ! ( cqes[ 0 ] . result( ) >= 0 ) ;
1629
+ let io_uring_socket = unsafe { Socket :: from_raw_fd ( cqes[ 0 ] . result ( ) ) } ;
1630
+ assert ! ( io_uring_socket. as_raw_fd( ) != plain_fd) ;
1631
+ assert_eq ! ( cqes[ 0 ] . flags( ) , 0 ) ;
1632
+
1633
+ // Try to bind.
1634
+ {
1635
+ let server_addr: std:: net:: SocketAddr = "127.0.0.1:0" . parse ( ) . unwrap ( ) ;
1636
+ let server_addr: socket2:: SockAddr = server_addr. into ( ) ;
1637
+ let op = io_uring:: opcode:: Bind :: new (
1638
+ io_uring:: types:: Fd ( io_uring_socket. as_raw_fd ( ) ) ,
1639
+ server_addr. as_ptr ( ) as * const _ ,
1640
+ server_addr. len ( ) ,
1641
+ )
1642
+ . build ( )
1643
+ . user_data ( 2345 ) ;
1644
+ unsafe {
1645
+ ring. submission ( ) . push ( & op. into ( ) ) . expect ( "queue is full" ) ;
1646
+ }
1647
+ ring. submit_and_wait ( 1 ) ?;
1648
+ let cqes: Vec < cqueue:: Entry > = ring. completion ( ) . map ( Into :: into) . collect ( ) ;
1649
+ assert_eq ! ( cqes. len( ) , 1 ) ;
1650
+ assert_eq ! ( cqes[ 0 ] . user_data( ) , 2345 ) ;
1651
+ assert_eq ! ( cqes[ 0 ] . result( ) , 0 ) ;
1652
+ assert_eq ! ( cqes[ 0 ] . flags( ) , 0 ) ;
1653
+
1654
+ assert_eq ! (
1655
+ io_uring_socket
1656
+ . local_addr( )
1657
+ . expect( "no local addr" )
1658
+ . as_socket_ipv4( )
1659
+ . expect( "no IPv4 address" )
1660
+ . ip( ) ,
1661
+ server_addr. as_socket_ipv4( ) . unwrap( ) . ip( )
1662
+ ) ;
1663
+ }
1664
+
1665
+ // Try to listen.
1666
+ {
1667
+ let op =
1668
+ io_uring:: opcode:: Listen :: new ( io_uring:: types:: Fd ( io_uring_socket. as_raw_fd ( ) ) , 128 )
1669
+ . build ( )
1670
+ . user_data ( 3456 ) ;
1671
+ unsafe {
1672
+ ring. submission ( ) . push ( & op. into ( ) ) . expect ( "queue is full" ) ;
1673
+ }
1674
+ ring. submit_and_wait ( 1 ) ?;
1675
+ let cqes: Vec < cqueue:: Entry > = ring. completion ( ) . map ( Into :: into) . collect ( ) ;
1676
+ assert_eq ! ( cqes. len( ) , 1 ) ;
1677
+ assert_eq ! ( cqes[ 0 ] . user_data( ) , 3456 ) ;
1678
+ assert_eq ! ( cqes[ 0 ] . result( ) , 0 ) ;
1679
+ assert_eq ! ( cqes[ 0 ] . flags( ) , 0 ) ;
1680
+
1681
+ // Ensure the socket is actually in the listening state.
1682
+ _ = TcpStream :: connect (
1683
+ io_uring_socket
1684
+ . local_addr ( )
1685
+ . unwrap ( )
1686
+ . as_socket_ipv4 ( )
1687
+ . unwrap ( ) ,
1688
+ ) ?;
1689
+ }
1690
+
1691
+ // Close both sockets, to avoid leaking FDs.
1692
+ drop ( plain_socket) ;
1693
+ drop ( io_uring_socket) ;
1694
+
1695
+ // Cleanup all fixed files (if any), then reserve slot 0.
1696
+ let _ = ring. submitter ( ) . unregister_files ( ) ;
1697
+ ring. submitter ( ) . register_files_sparse ( 1 ) . unwrap ( ) ;
1698
+
1699
+ let fixed_socket_op = opcode:: Socket :: new (
1700
+ Domain :: IPV4 . into ( ) ,
1701
+ Type :: DGRAM . into ( ) ,
1702
+ Protocol :: UDP . into ( ) ,
1703
+ ) ;
1704
+ let dest_slot = types:: DestinationSlot :: try_from_slot_target ( 0 ) . unwrap ( ) ;
1705
+ unsafe {
1706
+ ring. submission ( )
1707
+ . push (
1708
+ & fixed_socket_op
1709
+ . file_index ( Some ( dest_slot) )
1710
+ . build ( )
1711
+ . user_data ( 55 )
1712
+ . into ( ) ,
1713
+ )
1714
+ . expect ( "queue is full" ) ;
1715
+ }
1716
+ ring. submit_and_wait ( 1 ) ?;
1717
+
1718
+ let cqes: Vec < cqueue:: Entry > = ring. completion ( ) . map ( Into :: into) . collect ( ) ;
1719
+ assert_eq ! ( cqes. len( ) , 1 ) ;
1720
+ assert_eq ! ( cqes[ 0 ] . user_data( ) , 55 ) ;
1721
+ assert_eq ! ( cqes[ 0 ] . result( ) , 0 ) ;
1722
+ assert_eq ! ( cqes[ 0 ] . flags( ) , 0 ) ;
1723
+
1724
+ // If the fixed-socket operation worked properly, this must not fail.
1725
+ ring. submitter ( ) . unregister_files ( ) . unwrap ( ) ;
1726
+
1727
+ Ok ( ( ) )
1728
+ }
1729
+
1593
1730
pub fn test_udp_recvmsg_multishot < S : squeue:: EntryMarker , C : cqueue:: EntryMarker > (
1594
1731
ring : & mut IoUring < S , C > ,
1595
1732
test : & Test ,
0 commit comments