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.io.IOException;
25  import java.net.InetSocketAddress;
26  import java.nio.ByteBuffer;
27  import java.nio.ByteOrder;
28  
29  final class MsgHdrMemory {
30      public static final int MSG_HDR_SIZE =
31              Native.SIZEOF_MSGHDR + Native.SIZEOF_SOCKADDR_STORAGE + Native.SIZEOF_IOVEC + Native.CMSG_SPACE;
32      private static final byte[] EMPTY_SOCKADDR_STORAGE = new byte[Native.SIZEOF_SOCKADDR_STORAGE];
33      // It is not possible to have a zero length buffer in sendFd,
34      // so we use a 1 byte buffer here.
35      private static final int GLOBAL_IOV_LEN = 1;
36      private static final ByteBuffer GLOBAL_IOV_BASE =  Buffer.allocateDirectWithNativeOrder(GLOBAL_IOV_LEN);
37      private static final long GLOBAL_IOV_BASE_ADDRESS = Buffer.memoryAddress(GLOBAL_IOV_BASE);
38      private final CleanableDirectBuffer msgHdrMemoryCleanable;
39      private final CleanableDirectBuffer socketAddrMemoryCleanable;
40      private final CleanableDirectBuffer iovMemoryCleanable;
41      private final CleanableDirectBuffer cmsgDataMemoryCleanable;
42      private final ByteBuffer msgHdrMemory;
43      private final ByteBuffer socketAddrMemory;
44      private final ByteBuffer iovMemory;
45      private final ByteBuffer cmsgDataMemory;
46  
47      private final long msgHdrMemoryAddress;
48      private final short idx;
49      private final int cmsgDataOffset;
50  
51      MsgHdrMemory(short idx, ByteBuffer msgHdrMemoryArray) {
52          this.idx = idx;
53          this.msgHdrMemoryCleanable = null;
54          this.socketAddrMemoryCleanable = null;
55          this.iovMemoryCleanable = null;
56          this.cmsgDataMemoryCleanable = null;
57          int offset = idx * MSG_HDR_SIZE;
58          // ByteBuffer.slice(int, int) / duplicate() are specified to produce BIG_ENDIAN byte buffers.
59          // Set native order explicitly so native structs written via putInt/putLong use the expected endianness.
60          this.msgHdrMemory = PlatformDependent.offsetSlice(
61                  msgHdrMemoryArray, offset, Native.SIZEOF_MSGHDR
62          ).order(ByteOrder.nativeOrder());
63          offset += Native.SIZEOF_MSGHDR;
64          this.socketAddrMemory = PlatformDependent.offsetSlice(
65                  msgHdrMemoryArray, offset, Native.SIZEOF_SOCKADDR_STORAGE
66          ).order(ByteOrder.nativeOrder());
67          offset += Native.SIZEOF_SOCKADDR_STORAGE;
68          this.iovMemory = PlatformDependent.offsetSlice(
69                  msgHdrMemoryArray, offset, Native.SIZEOF_IOVEC
70          ).order(ByteOrder.nativeOrder());
71          offset += Native.SIZEOF_IOVEC;
72          this.cmsgDataMemory = PlatformDependent.offsetSlice(
73                  msgHdrMemoryArray, offset, Native.CMSG_SPACE
74          ).order(ByteOrder.nativeOrder());
75  
76          msgHdrMemoryAddress = Buffer.memoryAddress(msgHdrMemory);
77  
78          long cmsgDataMemoryAddr = Buffer.memoryAddress(cmsgDataMemory);
79          long cmsgDataAddr = Native.cmsghdrData(cmsgDataMemoryAddr);
80          cmsgDataOffset = (int) (cmsgDataAddr - cmsgDataMemoryAddr);
81      }
82  
83      MsgHdrMemory() {
84          this.idx = 0;
85          // jdk will memset the memory to 0, so we don't need to do it here.
86          msgHdrMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_MSGHDR);
87          socketAddrMemoryCleanable = null;
88          iovMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.SIZEOF_IOVEC);
89          cmsgDataMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(Native.CMSG_SPACE_FOR_FD);
90  
91          msgHdrMemory = msgHdrMemoryCleanable.buffer();
92          socketAddrMemory = null;
93          iovMemory = iovMemoryCleanable.buffer();
94          cmsgDataMemory = cmsgDataMemoryCleanable.buffer();
95  
96          msgHdrMemoryAddress = Buffer.memoryAddress(msgHdrMemory);
97          // These two parameters must be set to valid values and cannot be 0,
98          // otherwise the fd we get in io_uring_recvmsg is 0
99          Iov.set(iovMemory, GLOBAL_IOV_BASE_ADDRESS, GLOBAL_IOV_LEN);
100 
101         long cmsgDataMemoryAddr = Buffer.memoryAddress(cmsgDataMemory);
102         long cmsgDataAddr = Native.cmsghdrData(cmsgDataMemoryAddr);
103         cmsgDataOffset = (int) (cmsgDataAddr - cmsgDataMemoryAddr);
104     }
105 
106     void set(LinuxSocket socket, InetSocketAddress address, long bufferAddress , int length, short segmentSize) {
107         int addressLength = setSocketAddress(socket, address);
108         Iov.set(iovMemory, bufferAddress, length);
109         MsgHdr.set(msgHdrMemory, socketAddrMemory, addressLength, iovMemory, 1, cmsgDataMemory,
110                 cmsgDataOffset, segmentSize);
111     }
112 
113     void set(long iovArray, int length) {
114         MsgHdr.set(msgHdrMemory, iovArray, length);
115     }
116 
117     void setWithIovArrayAddress(LinuxSocket socket, InetSocketAddress address,
118                                 long iovArrayAddress, int iovArrayLength, short segmentSize) {
119         int addressLength = setSocketAddress(socket, address);
120         MsgHdr.set(msgHdrMemory, socketAddrMemory, addressLength, iovArrayAddress, iovArrayLength,
121                 cmsgDataMemory, cmsgDataOffset, segmentSize);
122     }
123 
124     private int setSocketAddress(LinuxSocket socket, InetSocketAddress address) {
125         int addressLength;
126         if (address == null) {
127             addressLength = socket.isIpv6() ? Native.SIZEOF_SOCKADDR_IN6 : Native.SIZEOF_SOCKADDR_IN;
128             socketAddrMemory.mark();
129             try {
130                 socketAddrMemory.put(EMPTY_SOCKADDR_STORAGE);
131             } finally {
132                 socketAddrMemory.reset();
133             }
134         } else {
135             addressLength = SockaddrIn.set(socket.isIpv6(), socketAddrMemory, address);
136         }
137         return addressLength;
138     }
139 
140     void setScmRightsFd(int fd) {
141         MsgHdr.prepSendFd(msgHdrMemory, fd, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
142     }
143 
144     int getScmRightsFd() throws IOException {
145         return MsgHdr.getCmsgData(msgHdrMemory, cmsgDataMemory, cmsgDataOffset);
146     }
147 
148     void prepRecvReadFd() {
149         MsgHdr.prepReadFd(msgHdrMemory, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
150     }
151 
152     boolean hasPort(IoUringDatagramChannel channel) {
153         if (channel.socket.isIpv6()) {
154             return SockaddrIn.hasPortIpv6(socketAddrMemory);
155         }
156         return SockaddrIn.hasPortIpv4(socketAddrMemory);
157     }
158 
159     DatagramPacket get(IoUringDatagramChannel channel, IoUringIoHandler handler, ByteBuf buffer, int bytesRead) {
160         InetSocketAddress sender;
161         if (channel.socket.isIpv6()) {
162             byte[] ipv6Bytes = handler.inet6AddressArray();
163             byte[] ipv4bytes = handler.inet4AddressArray();
164 
165             sender = SockaddrIn.getIPv6(socketAddrMemory, ipv6Bytes, ipv4bytes);
166         } else {
167             byte[] bytes = handler.inet4AddressArray();
168             sender = SockaddrIn.getIPv4(socketAddrMemory, bytes);
169         }
170         long bufferAddress = Iov.getBufferAddress(iovMemory);
171         int bufferLength = Iov.getBufferLength(iovMemory);
172         // reconstruct the reader index based on the memoryAddress of the buffer and the bufferAddress that was used
173         // in the iovec.
174         long memoryAddress = IoUring.memoryAddress(buffer);
175         int readerIndex = (int) (bufferAddress - memoryAddress);
176 
177         ByteBuf slice = buffer.slice(readerIndex, bufferLength)
178                 .writerIndex(bytesRead);
179         return new DatagramPacket(slice.retain(), channel.localAddress(), sender);
180     }
181 
182     short idx() {
183         return idx;
184     }
185 
186     long address() {
187         return msgHdrMemoryAddress;
188     }
189 
190     void release() {
191         if (msgHdrMemoryCleanable != null) {
192             msgHdrMemoryCleanable.clean();
193         }
194         if (socketAddrMemoryCleanable != null) {
195             socketAddrMemoryCleanable.clean();
196         }
197         if (iovMemoryCleanable != null) {
198             iovMemoryCleanable.clean();
199         }
200         if (cmsgDataMemoryCleanable != null) {
201             cmsgDataMemoryCleanable.clean();
202         }
203     }
204 }