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