View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.uring;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.socket.DatagramPacket;
20  import io.netty.channel.unix.Buffer;
21  import io.netty.util.internal.CleanableDirectBuffer;
22  import io.netty.util.internal.PlatformDependent;
23  
24  import java.net.InetSocketAddress;
25  import java.nio.ByteBuffer;
26  import java.nio.ByteOrder;
27  
28  final class MsgHdrMemory {
29      public static final int MSG_HDR_SIZE =
30              Native.SIZEOF_MSGHDR + Native.SIZEOF_SOCKADDR_STORAGE + Native.SIZEOF_IOVEC + Native.CMSG_SPACE;
31      private static final byte[] EMPTY_SOCKADDR_STORAGE = new byte[Native.SIZEOF_SOCKADDR_STORAGE];
32      // It is not possible to have a zero length buffer in sendFd,
33      // so we use a 1 byte buffer here.
34      private static final int GLOBAL_IOV_LEN = 1;
35      private static final ByteBuffer GLOBAL_IOV_BASE =  Buffer.allocateDirectWithNativeOrder(GLOBAL_IOV_LEN);
36      private static final long GLOBAL_IOV_BASE_ADDRESS = Buffer.memoryAddress(GLOBAL_IOV_BASE);
37      private final CleanableDirectBuffer msgHdrMemoryCleanable;
38      private final CleanableDirectBuffer socketAddrMemoryCleanable;
39      private final CleanableDirectBuffer iovMemoryCleanable;
40      private final CleanableDirectBuffer cmsgDataMemoryCleanable;
41      private final ByteBuffer msgHdrMemory;
42      private final ByteBuffer socketAddrMemory;
43      private final ByteBuffer iovMemory;
44      private final ByteBuffer cmsgDataMemory;
45  
46      private final long msgHdrMemoryAddress;
47      private final short idx;
48      private final int cmsgDataOffset;
49  
50      MsgHdrMemory(short idx, ByteBuffer msgHdrMemoryArray) {
51          this.idx = idx;
52          this.msgHdrMemoryCleanable = null;
53          this.socketAddrMemoryCleanable = null;
54          this.iovMemoryCleanable = null;
55          this.cmsgDataMemoryCleanable = null;
56          int offset = idx * MSG_HDR_SIZE;
57          // ByteBuffer.slice(int, int) / duplicate() are specified to produce BIG_ENDIAN byte buffers.
58          // Set native order explicitly so native structs written via putInt/putLong use the expected endianness.
59          this.msgHdrMemory = PlatformDependent.offsetSlice(
60                  msgHdrMemoryArray, offset, Native.SIZEOF_MSGHDR
61          ).order(ByteOrder.nativeOrder());
62          offset += Native.SIZEOF_MSGHDR;
63          this.socketAddrMemory = PlatformDependent.offsetSlice(
64                  msgHdrMemoryArray, offset, Native.SIZEOF_SOCKADDR_STORAGE
65          ).order(ByteOrder.nativeOrder());
66          offset += Native.SIZEOF_SOCKADDR_STORAGE;
67          this.iovMemory = PlatformDependent.offsetSlice(
68                  msgHdrMemoryArray, offset, Native.SIZEOF_IOVEC
69          ).order(ByteOrder.nativeOrder());
70          offset += Native.SIZEOF_IOVEC;
71          this.cmsgDataMemory = PlatformDependent.offsetSlice(
72                  msgHdrMemoryArray, offset, Native.CMSG_SPACE
73          ).order(ByteOrder.nativeOrder());
74  
75          msgHdrMemoryAddress = Buffer.memoryAddress(msgHdrMemory);
76  
77          long cmsgDataMemoryAddr = Buffer.memoryAddress(cmsgDataMemory);
78          long cmsgDataAddr = Native.cmsghdrData(cmsgDataMemoryAddr);
79          cmsgDataOffset = (int) (cmsgDataAddr - cmsgDataMemoryAddr);
80      }
81  
82      MsgHdrMemory() {
83          this.idx = 0;
84          // jdk will memset the memory to 0, so we don't need to do it here.
85          msgHdrMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_MSGHDR);
86          socketAddrMemoryCleanable = null;
87          iovMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_IOVEC);
88          cmsgDataMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.CMSG_SPACE_FOR_FD);
89  
90          msgHdrMemory = msgHdrMemoryCleanable.buffer();
91          socketAddrMemory = null;
92          iovMemory = iovMemoryCleanable.buffer();
93          cmsgDataMemory = cmsgDataMemoryCleanable.buffer();
94  
95          msgHdrMemoryAddress = Buffer.memoryAddress(msgHdrMemory);
96          // These two parameters must be set to valid values and cannot be 0,
97          // otherwise the fd we get in io_uring_recvmsg is 0
98          Iov.set(iovMemory, GLOBAL_IOV_BASE_ADDRESS, GLOBAL_IOV_LEN);
99  
100         long cmsgDataMemoryAddr = Buffer.memoryAddress(cmsgDataMemory);
101         long cmsgDataAddr = Native.cmsghdrData(cmsgDataMemoryAddr);
102         cmsgDataOffset = (int) (cmsgDataAddr - cmsgDataMemoryAddr);
103     }
104 
105     void set(LinuxSocket socket, InetSocketAddress address, long bufferAddress , int length, short segmentSize) {
106         int addressLength;
107         if (address == null) {
108             addressLength = socket.isIpv6() ? Native.SIZEOF_SOCKADDR_IN6 : Native.SIZEOF_SOCKADDR_IN;
109             socketAddrMemory.mark();
110             try {
111                 socketAddrMemory.put(EMPTY_SOCKADDR_STORAGE);
112             } finally {
113                 socketAddrMemory.reset();
114             }
115         } else {
116             addressLength = SockaddrIn.set(socket.isIpv6(), socketAddrMemory, address);
117         }
118         Iov.set(iovMemory, bufferAddress, length);
119         MsgHdr.set(msgHdrMemory, socketAddrMemory, addressLength, iovMemory, 1, cmsgDataMemory,
120                 cmsgDataOffset, segmentSize);
121     }
122 
123     void set(long iovArray, int length) {
124         MsgHdr.set(msgHdrMemory, iovArray, length);
125     }
126 
127     void setScmRightsFd(int fd) {
128         MsgHdr.prepSendFd(msgHdrMemory, fd, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
129     }
130 
131     int getScmRightsFd() {
132         return MsgHdr.getCmsgData(msgHdrMemory, cmsgDataMemory, cmsgDataOffset);
133     }
134 
135     void prepRecvReadFd() {
136         MsgHdr.prepReadFd(msgHdrMemory, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
137     }
138 
139     boolean hasPort(IoUringDatagramChannel channel) {
140         if (channel.socket.isIpv6()) {
141             return SockaddrIn.hasPortIpv6(socketAddrMemory);
142         }
143         return SockaddrIn.hasPortIpv4(socketAddrMemory);
144     }
145 
146     DatagramPacket get(IoUringDatagramChannel channel, IoUringIoHandler handler, ByteBuf buffer, int bytesRead) {
147         InetSocketAddress sender;
148         if (channel.socket.isIpv6()) {
149             byte[] ipv6Bytes = handler.inet6AddressArray();
150             byte[] ipv4bytes = handler.inet4AddressArray();
151 
152             sender = SockaddrIn.getIPv6(socketAddrMemory, ipv6Bytes, ipv4bytes);
153         } else {
154             byte[] bytes = handler.inet4AddressArray();
155             sender = SockaddrIn.getIPv4(socketAddrMemory, bytes);
156         }
157         long bufferAddress = Iov.getBufferAddress(iovMemory);
158         int bufferLength = Iov.getBufferLength(iovMemory);
159         // reconstruct the reader index based on the memoryAddress of the buffer and the bufferAddress that was used
160         // in the iovec.
161         long memoryAddress = IoUring.memoryAddress(buffer);
162         int readerIndex = (int) (bufferAddress - memoryAddress);
163 
164         ByteBuf slice = buffer.slice(readerIndex, bufferLength)
165                 .writerIndex(bytesRead);
166         return new DatagramPacket(slice.retain(), channel.localAddress(), sender);
167     }
168 
169     short idx() {
170         return idx;
171     }
172 
173     long address() {
174         return msgHdrMemoryAddress;
175     }
176 
177     void release() {
178         if (msgHdrMemoryCleanable != null) {
179             msgHdrMemoryCleanable.clean();
180         }
181         if (socketAddrMemoryCleanable != null) {
182             socketAddrMemoryCleanable.clean();
183         }
184         if (iovMemoryCleanable != null) {
185             iovMemoryCleanable.clean();
186         }
187         if (cmsgDataMemoryCleanable != null) {
188             cmsgDataMemoryCleanable.clean();
189         }
190     }
191 }