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 = setSocketAddress(socket, address);
107         Iov.set(iovMemory, bufferAddress, length);
108         MsgHdr.set(msgHdrMemory, socketAddrMemory, addressLength, iovMemory, 1, cmsgDataMemory,
109                 cmsgDataOffset, segmentSize);
110     }
111 
112     void set(long iovArray, int length) {
113         MsgHdr.set(msgHdrMemory, iovArray, length);
114     }
115 
116     void setWithIovArrayAddress(LinuxSocket socket, InetSocketAddress address,
117                                 long iovArrayAddress, int iovArrayLength, short segmentSize) {
118         int addressLength = setSocketAddress(socket, address);
119         MsgHdr.set(msgHdrMemory, socketAddrMemory, addressLength, iovArrayAddress, iovArrayLength,
120                 cmsgDataMemory, cmsgDataOffset, segmentSize);
121     }
122 
123     private int setSocketAddress(LinuxSocket socket, InetSocketAddress address) {
124         int addressLength;
125         if (address == null) {
126             addressLength = socket.isIpv6() ? Native.SIZEOF_SOCKADDR_IN6 : Native.SIZEOF_SOCKADDR_IN;
127             socketAddrMemory.mark();
128             try {
129                 socketAddrMemory.put(EMPTY_SOCKADDR_STORAGE);
130             } finally {
131                 socketAddrMemory.reset();
132             }
133         } else {
134             addressLength = SockaddrIn.set(socket.isIpv6(), socketAddrMemory, address);
135         }
136         return addressLength;
137     }
138 
139     void setScmRightsFd(int fd) {
140         MsgHdr.prepSendFd(msgHdrMemory, fd, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
141     }
142 
143     int getScmRightsFd() {
144         return MsgHdr.getCmsgData(msgHdrMemory, cmsgDataMemory, cmsgDataOffset);
145     }
146 
147     void prepRecvReadFd() {
148         MsgHdr.prepReadFd(msgHdrMemory, cmsgDataMemory, cmsgDataOffset, iovMemory, 1);
149     }
150 
151     boolean hasPort(IoUringDatagramChannel channel) {
152         if (channel.socket.isIpv6()) {
153             return SockaddrIn.hasPortIpv6(socketAddrMemory);
154         }
155         return SockaddrIn.hasPortIpv4(socketAddrMemory);
156     }
157 
158     DatagramPacket get(IoUringDatagramChannel channel, IoUringIoHandler handler, ByteBuf buffer, int bytesRead) {
159         InetSocketAddress sender;
160         if (channel.socket.isIpv6()) {
161             byte[] ipv6Bytes = handler.inet6AddressArray();
162             byte[] ipv4bytes = handler.inet4AddressArray();
163 
164             sender = SockaddrIn.getIPv6(socketAddrMemory, ipv6Bytes, ipv4bytes);
165         } else {
166             byte[] bytes = handler.inet4AddressArray();
167             sender = SockaddrIn.getIPv4(socketAddrMemory, bytes);
168         }
169         long bufferAddress = Iov.getBufferAddress(iovMemory);
170         int bufferLength = Iov.getBufferLength(iovMemory);
171         // reconstruct the reader index based on the memoryAddress of the buffer and the bufferAddress that was used
172         // in the iovec.
173         long memoryAddress = IoUring.memoryAddress(buffer);
174         int readerIndex = (int) (bufferAddress - memoryAddress);
175 
176         ByteBuf slice = buffer.slice(readerIndex, bufferLength)
177                 .writerIndex(bytesRead);
178         return new DatagramPacket(slice.retain(), channel.localAddress(), sender);
179     }
180 
181     short idx() {
182         return idx;
183     }
184 
185     long address() {
186         return msgHdrMemoryAddress;
187     }
188 
189     void release() {
190         if (msgHdrMemoryCleanable != null) {
191             msgHdrMemoryCleanable.clean();
192         }
193         if (socketAddrMemoryCleanable != null) {
194             socketAddrMemoryCleanable.clean();
195         }
196         if (iovMemoryCleanable != null) {
197             iovMemoryCleanable.clean();
198         }
199         if (cmsgDataMemoryCleanable != null) {
200             cmsgDataMemoryCleanable.clean();
201         }
202     }
203 }