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.buffer.ByteBufAllocator;
20  import io.netty.channel.ChannelException;
21  import io.netty.channel.ChannelOption;
22  import io.netty.channel.FixedRecvByteBufAllocator;
23  import io.netty.channel.MessageSizeEstimator;
24  import io.netty.channel.RecvByteBufAllocator;
25  import io.netty.channel.WriteBufferWaterMark;
26  import io.netty.channel.socket.DatagramChannelConfig;
27  import io.netty.util.internal.ObjectUtil;
28  
29  import java.io.IOException;
30  import java.net.InetAddress;
31  import java.net.NetworkInterface;
32  import java.util.Map;
33  
34  final class IoUringDatagramChannelConfig extends IoUringChannelConfig implements DatagramChannelConfig {
35      private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048);
36      private boolean activeOnOpen;
37      private volatile int maxDatagramSize;
38  
39      IoUringDatagramChannelConfig(AbstractIoUringChannel channel) {
40          super(channel);
41          setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR);
42      }
43  
44      @Override
45      @SuppressWarnings("deprecation")
46      public Map<ChannelOption<?>, Object> getOptions() {
47          return getOptions(
48                  super.getOptions(),
49                  ChannelOption.SO_BROADCAST, ChannelOption.SO_RCVBUF, ChannelOption.SO_SNDBUF,
50                  ChannelOption.SO_REUSEADDR, ChannelOption.IP_MULTICAST_LOOP_DISABLED,
51                  ChannelOption.IP_MULTICAST_ADDR, ChannelOption.IP_MULTICAST_IF, ChannelOption.IP_MULTICAST_TTL,
52                  ChannelOption.IP_TOS, ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION,
53                  IoUringChannelOption.SO_REUSEPORT, IoUringChannelOption.IP_FREEBIND,
54                  IoUringChannelOption.IP_TRANSPARENT, IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE);
55      }
56  
57      @SuppressWarnings({ "unchecked", "deprecation" })
58      @Override
59      public <T> T getOption(ChannelOption<T> option) {
60          if (option == ChannelOption.SO_BROADCAST) {
61              return (T) Boolean.valueOf(isBroadcast());
62          }
63          if (option == ChannelOption.SO_RCVBUF) {
64              return (T) Integer.valueOf(getReceiveBufferSize());
65          }
66          if (option == ChannelOption.SO_SNDBUF) {
67              return (T) Integer.valueOf(getSendBufferSize());
68          }
69          if (option == ChannelOption.SO_REUSEADDR) {
70              return (T) Boolean.valueOf(isReuseAddress());
71          }
72          if (option == ChannelOption.IP_MULTICAST_LOOP_DISABLED) {
73              return (T) Boolean.valueOf(isLoopbackModeDisabled());
74          }
75          if (option == ChannelOption.IP_MULTICAST_ADDR) {
76              return (T) getInterface();
77          }
78          if (option == ChannelOption.IP_MULTICAST_IF) {
79              return (T) getNetworkInterface();
80          }
81          if (option == ChannelOption.IP_MULTICAST_TTL) {
82              return (T) Integer.valueOf(getTimeToLive());
83          }
84          if (option == ChannelOption.IP_TOS) {
85              return (T) Integer.valueOf(getTrafficClass());
86          }
87          if (option == ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
88              return (T) Boolean.valueOf(activeOnOpen);
89          }
90          if (option == IoUringChannelOption.SO_REUSEPORT) {
91              return (T) Boolean.valueOf(isReusePort());
92          }
93          if (option == IoUringChannelOption.IP_TRANSPARENT) {
94              return (T) Boolean.valueOf(isIpTransparent());
95          }
96          if (option == IoUringChannelOption.IP_FREEBIND) {
97              return (T) Boolean.valueOf(isFreeBind());
98          }
99          if (option == IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE) {
100             return (T) Integer.valueOf(getMaxDatagramPayloadSize());
101         }
102         return super.getOption(option);
103     }
104 
105     @Override
106     @SuppressWarnings("deprecation")
107     public <T> boolean setOption(ChannelOption<T> option, T value) {
108         validate(option, value);
109 
110         if (option == ChannelOption.SO_BROADCAST) {
111             setBroadcast((Boolean) value);
112         } else if (option == ChannelOption.SO_RCVBUF) {
113             setReceiveBufferSize((Integer) value);
114         } else if (option == ChannelOption.SO_SNDBUF) {
115             setSendBufferSize((Integer) value);
116         } else if (option == ChannelOption.SO_REUSEADDR) {
117             setReuseAddress((Boolean) value);
118         } else if (option == ChannelOption.IP_MULTICAST_LOOP_DISABLED) {
119             setLoopbackModeDisabled((Boolean) value);
120         } else if (option == ChannelOption.IP_MULTICAST_ADDR) {
121             setInterface((InetAddress) value);
122         } else if (option == ChannelOption.IP_MULTICAST_IF) {
123             setNetworkInterface((NetworkInterface) value);
124         } else if (option == ChannelOption.IP_MULTICAST_TTL) {
125             setTimeToLive((Integer) value);
126         } else if (option == ChannelOption.IP_TOS) {
127             setTrafficClass((Integer) value);
128         } else if (option == ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) {
129             setActiveOnOpen((Boolean) value);
130         } else if (option == IoUringChannelOption.SO_REUSEPORT) {
131             setReusePort((Boolean) value);
132         } else if (option == IoUringChannelOption.IP_FREEBIND) {
133             setFreeBind((Boolean) value);
134         } else if (option == IoUringChannelOption.IP_TRANSPARENT) {
135             setIpTransparent((Boolean) value);
136         } else if (option == IoUringChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE) {
137             setMaxDatagramPayloadSize((Integer) value);
138         } else {
139             return super.setOption(option, value);
140         }
141 
142         return true;
143     }
144 
145     private void setActiveOnOpen(boolean activeOnOpen) {
146         if (channel.isRegistered()) {
147             throw new IllegalStateException("Can only changed before channel was registered");
148         }
149         this.activeOnOpen = activeOnOpen;
150     }
151 
152     boolean getActiveOnOpen() {
153         return activeOnOpen;
154     }
155 
156     @Override
157     public IoUringDatagramChannelConfig setMessageSizeEstimator(MessageSizeEstimator estimator) {
158         super.setMessageSizeEstimator(estimator);
159         return this;
160     }
161 
162     @Override
163     @Deprecated
164     public IoUringDatagramChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) {
165         super.setWriteBufferLowWaterMark(writeBufferLowWaterMark);
166         return this;
167     }
168 
169     @Override
170     @Deprecated
171     public IoUringDatagramChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) {
172         super.setWriteBufferHighWaterMark(writeBufferHighWaterMark);
173         return this;
174     }
175 
176     @Override
177     public IoUringDatagramChannelConfig setWriteBufferWaterMark(WriteBufferWaterMark writeBufferWaterMark) {
178         super.setWriteBufferWaterMark(writeBufferWaterMark);
179         return this;
180     }
181 
182     @Override
183     public IoUringDatagramChannelConfig setAutoClose(boolean autoClose) {
184         super.setAutoClose(autoClose);
185         return this;
186     }
187 
188     @Override
189     public IoUringDatagramChannelConfig setAutoRead(boolean autoRead) {
190         super.setAutoRead(autoRead);
191         return this;
192     }
193 
194     @Override
195     public IoUringDatagramChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) {
196         super.setRecvByteBufAllocator(allocator);
197         return this;
198     }
199 
200     @Override
201     public IoUringDatagramChannelConfig setWriteSpinCount(int writeSpinCount) {
202         super.setWriteSpinCount(writeSpinCount);
203         return this;
204     }
205 
206     @Override
207     public IoUringDatagramChannelConfig setAllocator(ByteBufAllocator allocator) {
208         super.setAllocator(allocator);
209         return this;
210     }
211 
212     @Override
213     public IoUringDatagramChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
214         super.setConnectTimeoutMillis(connectTimeoutMillis);
215         return this;
216     }
217 
218     @Override
219     @Deprecated
220     public IoUringDatagramChannelConfig setMaxMessagesPerRead(int maxMessagesPerRead) {
221         super.setMaxMessagesPerRead(maxMessagesPerRead);
222         return this;
223     }
224 
225     @Override
226     public int getSendBufferSize() {
227         try {
228             return ((AbstractIoUringChannel) channel).socket.getSendBufferSize();
229         } catch (IOException e) {
230             throw new ChannelException(e);
231         }
232     }
233 
234     @Override
235     public IoUringDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
236         try {
237             ((AbstractIoUringChannel) channel).socket.setSendBufferSize(sendBufferSize);
238             return this;
239         } catch (IOException e) {
240             throw new ChannelException(e);
241         }
242     }
243 
244     @Override
245     public int getReceiveBufferSize() {
246         try {
247             return ((AbstractIoUringChannel) channel).socket.getReceiveBufferSize();
248         } catch (IOException e) {
249             throw new ChannelException(e);
250         }
251     }
252 
253     @Override
254     public IoUringDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
255         try {
256             ((AbstractIoUringChannel) channel).socket.setReceiveBufferSize(receiveBufferSize);
257             return this;
258         } catch (IOException e) {
259             throw new ChannelException(e);
260         }
261     }
262 
263     @Override
264     public int getTrafficClass() {
265         try {
266             return ((AbstractIoUringChannel) channel).socket.getTrafficClass();
267         } catch (IOException e) {
268             throw new ChannelException(e);
269         }
270     }
271 
272     @Override
273     public IoUringDatagramChannelConfig setTrafficClass(int trafficClass) {
274         try {
275             ((AbstractIoUringChannel) channel).socket.setTrafficClass(trafficClass);
276             return this;
277         } catch (IOException e) {
278             throw new ChannelException(e);
279         }
280     }
281 
282     @Override
283     public boolean isReuseAddress() {
284         try {
285             return ((AbstractIoUringChannel) channel).socket.isReuseAddress();
286         } catch (IOException e) {
287             throw new ChannelException(e);
288         }
289     }
290 
291     @Override
292     public IoUringDatagramChannelConfig setReuseAddress(boolean reuseAddress) {
293         try {
294             ((AbstractIoUringChannel) channel).socket.setReuseAddress(reuseAddress);
295             return this;
296         } catch (IOException e) {
297             throw new ChannelException(e);
298         }
299     }
300 
301     @Override
302     public boolean isBroadcast() {
303         try {
304             return ((AbstractIoUringChannel) channel).socket.isBroadcast();
305         } catch (IOException e) {
306             throw new ChannelException(e);
307         }
308     }
309 
310     @Override
311     public IoUringDatagramChannelConfig setBroadcast(boolean broadcast) {
312         try {
313             ((AbstractIoUringChannel) channel).socket.setBroadcast(broadcast);
314             return this;
315         } catch (IOException e) {
316             throw new ChannelException(e);
317         }
318     }
319 
320     @Override
321     public boolean isLoopbackModeDisabled() {
322         try {
323             return ((AbstractIoUringChannel) channel).socket.isLoopbackModeDisabled();
324         } catch (IOException e) {
325             throw new ChannelException(e);
326         }
327     }
328 
329     @Override
330     public IoUringDatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
331         try {
332             ((AbstractIoUringChannel) channel).socket.setLoopbackModeDisabled(loopbackModeDisabled);
333             return this;
334         } catch (IOException e) {
335             throw new ChannelException(e);
336         }
337     }
338 
339     @Override
340     public int getTimeToLive() {
341         try {
342             return ((AbstractIoUringChannel) channel).socket.getTimeToLive();
343         } catch (IOException e) {
344             throw new ChannelException(e);
345         }
346     }
347 
348     @Override
349     public IoUringDatagramChannelConfig setTimeToLive(int ttl) {
350         try {
351             ((AbstractIoUringChannel) channel).socket.setTimeToLive(ttl);
352             return this;
353         } catch (IOException e) {
354             throw new ChannelException(e);
355         }
356     }
357 
358     @Override
359     public InetAddress getInterface() {
360         try {
361             return ((AbstractIoUringChannel) channel).socket.getInterface();
362         } catch (IOException e) {
363             throw new ChannelException(e);
364         }
365     }
366 
367     @Override
368     public IoUringDatagramChannelConfig setInterface(InetAddress interfaceAddress) {
369         try {
370             ((AbstractIoUringChannel) channel).socket.setInterface(interfaceAddress);
371             return this;
372         } catch (IOException e) {
373             throw new ChannelException(e);
374         }
375     }
376 
377     @Override
378     public NetworkInterface getNetworkInterface() {
379         try {
380             return ((AbstractIoUringChannel) channel).socket.getNetworkInterface();
381         } catch (IOException e) {
382             throw new ChannelException(e);
383         }
384     }
385 
386     @Override
387     public IoUringDatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
388         try {
389             ((AbstractIoUringChannel) channel).socket.setNetworkInterface(networkInterface);
390             return this;
391         } catch (IOException e) {
392             throw new ChannelException(e);
393         }
394     }
395 
396     /**
397      * Returns {@code true} if the SO_REUSEPORT option is set.
398      */
399     public boolean isReusePort() {
400         try {
401             return ((AbstractIoUringChannel) channel).socket.isReusePort();
402         } catch (IOException e) {
403             throw new ChannelException(e);
404         }
405     }
406 
407     /**
408      * Set the SO_REUSEPORT option on the underlying Channel. This will allow to bind multiple
409      * {@link io.netty.channel.socket.DatagramChannel}s to the same port and so receive datagrams with multiple threads.
410      *
411      * Be aware this method needs be called before
412      * {@link io.netty.channel.socket.DatagramChannel#bind(java.net.SocketAddress)} to have
413      * any affect.
414      */
415     public IoUringDatagramChannelConfig setReusePort(boolean reusePort) {
416         try {
417             ((AbstractIoUringChannel) channel).socket.setReusePort(reusePort);
418             return this;
419         } catch (IOException e) {
420             throw new ChannelException(e);
421         }
422     }
423 
424     /**
425      * Returns {@code true} if <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
426      * {@code false} otherwise.
427      */
428     public boolean isIpTransparent() {
429         try {
430             return ((AbstractIoUringChannel) channel).socket.isIpTransparent();
431         } catch (IOException e) {
432             throw new ChannelException(e);
433         }
434     }
435 
436     /**
437      * If {@code true} is used <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_TRANSPARENT</a> is enabled,
438      * {@code false} for disable it. Default is disabled.
439      */
440     public IoUringDatagramChannelConfig setIpTransparent(boolean ipTransparent) {
441         try {
442             ((AbstractIoUringChannel) channel).socket.setIpTransparent(ipTransparent);
443             return this;
444         } catch (IOException e) {
445             throw new ChannelException(e);
446         }
447     }
448 
449     /**
450      * Returns {@code true} if <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
451      * {@code false} otherwise.
452      */
453     public boolean isFreeBind() {
454         try {
455             return ((AbstractIoUringChannel) channel).socket.isIpFreeBind();
456         } catch (IOException e) {
457             throw new ChannelException(e);
458         }
459     }
460 
461     /**
462      * If {@code true} is used <a href="https://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
463      * {@code false} for disable it. Default is disabled.
464      */
465     public IoUringDatagramChannelConfig setFreeBind(boolean freeBind) {
466         try {
467             ((AbstractIoUringChannel) channel).socket.setIpFreeBind(freeBind);
468             return this;
469         } catch (IOException e) {
470             throw new ChannelException(e);
471         }
472     }
473 
474     /**
475      * Set the maximum {@link io.netty.channel.socket.DatagramPacket} size. This will be used to determine if
476      * a batch of {@code IORING_IO_RECVMSG} should be used when reading from the underlying socket.
477      * When batched {@code recvmmsg} is used
478      * we may be able to read multiple {@link io.netty.channel.socket.DatagramPacket}s with one syscall and so
479      * greatly improve the performance. This number will be used to slice {@link ByteBuf}s returned by the used
480      * {@link RecvByteBufAllocator}. You can use {@code 0} to disable the usage of batching, any other bigger value
481      * will enable it.
482      */
483     public IoUringDatagramChannelConfig setMaxDatagramPayloadSize(int maxDatagramSize) {
484         this.maxDatagramSize = ObjectUtil.checkPositiveOrZero(maxDatagramSize, "maxDatagramSize");
485         return this;
486     }
487 
488     /**
489      * Get the maximum {@link io.netty.channel.socket.DatagramPacket} size.
490      */
491     public int getMaxDatagramPayloadSize() {
492         return maxDatagramSize;
493     }
494 }