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