Skip to content

Commit 6807c70

Browse files
author
kasemir
committed
PVA: Start version 3, encode/decode search flag reply src port
1 parent 49e516e commit 6807c70

4 files changed

Lines changed: 48 additions & 20 deletions

File tree

core/pva/TLS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ This is an example recipe for getting started.
3939
Note its "Certificate identifier":
4040

4141
```
42-
$ authnstd --name ioc --cert-usage hybrid
42+
$ authnstd --name ioc --cert-usage ioc
4343
Keychain file created : /home/user/.config/pva/1.3/server.p12
4444
Certificate identifier : e53ed409:15273288300286014953
4545
```

core/pva/src/main/java/org/epics/pva/client/ClientUDPHandler.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ public interface SearchResponseHandler
8787
// with the understanding that it will only receive broadcasts;
8888
// since they are often blocked by firewall, may receive nothing, ever.
8989
private final DatagramChannel udp_beacon;
90-
private final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
9190

9291
private volatile Thread search_thread4, search_thread6, beacon_thread;
9392

@@ -107,13 +106,15 @@ public ClientUDPHandler(final BeaconHandler beacon_handler,
107106

108107
// IPv6 sockets
109108
// Beacon socket only receives, does not send broadcasts
110-
if (PVASettings.EPICS_PVA_ENABLE_IPV6) {
109+
if (PVASettings.EPICS_PVA_ENABLE_IPV6)
110+
{
111111
udp_search6 = Network.createUDP(StandardProtocolFamily.INET6, null, 0);
112112
udp_localaddr6 = (InetSocketAddress) udp_search6.getLocalAddress();
113113
ipV6Msg = String.format(" and %s", udp_localaddr6);
114114
udp_beacon = Network.createUDP(StandardProtocolFamily.INET6, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
115115
}
116-
else {
116+
else
117+
{
117118
udp_search6 = null;
118119
udp_beacon = Network.createUDP(StandardProtocolFamily.INET, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
119120
udp_localaddr6 = null;
@@ -150,11 +151,8 @@ public void send(final ByteBuffer buffer, final AddressInfo info) throws Excepti
150151
}
151152
else
152153
{
153-
if (!PVASettings.EPICS_PVA_ENABLE_IPV6) {
154-
throw new Exception(
155-
"EPICS_PVA_ENABLE_IPV6 must be enabled to use IPv6 address!"
156-
);
157-
}
154+
if (!PVASettings.EPICS_PVA_ENABLE_IPV6)
155+
throw new Exception("EPICS_PVA_ENABLE_IPV6 must be enabled to use IPv6 address!");
158156

159157
synchronized (udp_search6)
160158
{
@@ -177,13 +175,15 @@ public void start()
177175
search_thread4.setDaemon(true);
178176
search_thread4.start();
179177

180-
if (PVASettings.EPICS_PVA_ENABLE_IPV6) {
178+
if (PVASettings.EPICS_PVA_ENABLE_IPV6)
179+
{
181180
final ByteBuffer receive_buffer6 = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
182181
search_thread6 = new Thread(() -> listen(udp_search6, receive_buffer6), "UDP6-receiver " + Network.getLocalAddress(udp_search6));
183182
search_thread6.setDaemon(true);
184183
search_thread6.start();
185184
}
186185

186+
final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
187187
beacon_thread = new Thread(() -> listen(udp_beacon, beacon_buffer), "UDP-beacon-receiver " + Network.getLocalAddress(udp_beacon));
188188
beacon_thread.setDaemon(true);
189189
beacon_thread.start();

core/pva/src/main/java/org/epics/pva/common/PVAHeader.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2019-2022 Oak Ridge National Laboratory.
2+
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -26,8 +26,12 @@ public class PVAHeader
2626
/** PVA protocol magic */
2727
public static final byte PVA_MAGIC = (byte)0xCA;
2828

29-
/** PVA protocol revision (implemented by this library) */
30-
public static final byte PVA_PROTOCOL_REVISION = 2;
29+
/** PVA protocol revision (implemented by this library)
30+
*
31+
* <br>v2: Server's Echo reply includes the request payload
32+
* <br>v3: SearchRequest FLAG_REPLY_SRC_PORT
33+
*/
34+
public static final byte PVA_PROTOCOL_REVISION = 3;
3135

3236
/** Oldest PVA protocol revision handled by this library */
3337
public static final byte REQUIRED_PVA_PROTOCOL_REVISION = 1;

core/pva/src/main/java/org/epics/pva/common/SearchRequest.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
2+
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -63,12 +63,29 @@ public String toString()
6363
}
6464
};
6565

66+
/** Server should reply with its GUID and empty CID list
67+
* even if it does not host any of the searched channels
68+
*/
69+
public static final byte FLAG_SEARCH_MUST_REPLY = 0x01;
70+
71+
/** Client should ignore the 'port' in the reply and
72+
* simply use the port of the 'source', that is the peer port
73+
* of the UDP message or TCP connection
74+
* @since Version 3
75+
*/
76+
public static final byte FLAG_REPLY_SRC_PORT = 0x02;
77+
78+
/** Indicates that search message was unicast */
79+
public static final byte FLAG_SEARCH_UNICAST = (byte)0x80;
80+
6681
/** Sequence number */
6782
public int seq;
6883
/** Is it a unicast? */
6984
public boolean unicast;
7085
/** Is reply required? */
7186
public boolean reply_required;
87+
/** Reply to source port instead of port listed in the search request? */
88+
public boolean reply_to_src_port;
7289
/** Address of client */
7390
public InetSocketAddress client;
7491
/** Use TLS, or plain TCP? */
@@ -106,10 +123,10 @@ public static SearchRequest decode(final InetSocketAddress from, final byte vers
106123
// Search Sequence ID
107124
search.seq = buffer.getInt();
108125

109-
// 0-bit for replyRequired, 7-th bit for "sent as unicast" (1)/"sent as broadcast/multicast" (0)
110126
final byte flags = buffer.get();
111-
search.unicast = (flags & 0x80) == 0x80;
112-
search.reply_required = (flags & 0x01) == 0x01;
127+
search.unicast = (flags & FLAG_SEARCH_UNICAST) == FLAG_SEARCH_UNICAST;
128+
search.reply_required = (flags & FLAG_SEARCH_MUST_REPLY) == FLAG_SEARCH_MUST_REPLY;
129+
search.reply_to_src_port = (flags & FLAG_REPLY_SRC_PORT) == FLAG_REPLY_SRC_PORT;
113130

114131
// reserved
115132
buffer.get();
@@ -127,7 +144,13 @@ public static SearchRequest decode(final InetSocketAddress from, final byte vers
127144
logger.log(Level.WARNING, "PVA Client " + from + " sent search #" + search.seq + " with invalid address");
128145
return null;
129146
}
130-
final int port = Short.toUnsignedInt(buffer.getShort());
147+
int port = Short.toUnsignedInt(buffer.getShort());
148+
// Since version 3, flag can ask us to ignore the reply port in the message
149+
// and instead use the peer's port.
150+
// This should help with NAT where we get the message from an intermediate
151+
// and need to reply via that same intermediate
152+
if (version >= 3 && search.reply_to_src_port)
153+
port = from.getPort();
131154

132155
// Use address from message unless it's a generic local address
133156
if (addr.isAnyLocalAddress() || port <= 0)
@@ -201,8 +224,9 @@ public static void encode(final boolean unicast, final int seq, final Collection
201224
// only the one started last will see the unicast.
202225
// Mark search message as unicast so that receiver will forward
203226
// it via local broadcast to other local listeners.
204-
// 0-bit for replyRequired, 7-th bit for "sent as unicast" (1)/"sent as broadcast/multicast" (0)
205-
buffer.put((byte) ((unicast ? 0x80 : 0x00) | (channels == null ? 0x01 : 0x00)));
227+
buffer.put((byte) ((unicast ? FLAG_SEARCH_UNICAST : 0x00) |
228+
((channels == null || channels.isEmpty()) ? FLAG_SEARCH_MUST_REPLY : 0x00) |
229+
FLAG_REPLY_SRC_PORT));
206230

207231
// reserved
208232
buffer.put((byte) 0);

0 commit comments

Comments
 (0)