Skip to content

Commit cae8061

Browse files
authored
Merge pull request #3434 from ControlSystemStudio/pva_search_reply_port
PVA: Version 3, replySrcPort, several fixes
2 parents 67f8428 + 30c1b22 commit cae8061

23 files changed

+507
-167
lines changed

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/PVASettings.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,12 @@ public class PVASettings
122122
* <p>Next, multicast groups may be added.
123123
* Each multicast group must include an interface.
124124
* <pre>
125-
* 224.0.1.1,[email protected] - Listen to local IPv4 multicasts
125+
* 224.0.0.128,[email protected] - Listen to local IPv4 multicasts
126126
* [ff02::42:1],1@::1 - Listen to local IPv6 multicasts
127127
* [ff02::42:1],1@en1 - Listen to IPv6 multicasts on network interface en1
128128
* </pre>
129129
*/
130-
public static String EPICS_PVAS_INTF_ADDR_LIST = "0.0.0.0 [::] 224.0.1.1,[email protected] [ff02::42:1],1@::1";
130+
public static String EPICS_PVAS_INTF_ADDR_LIST = "0.0.0.0 [::] 224.0.0.128,[email protected] [ff02::42:1],1@::1";
131131

132132
/** PVA server port for name searches and beacons */
133133
public static int EPICS_PVAS_BROADCAST_PORT = EPICS_PVA_BROADCAST_PORT;
@@ -250,11 +250,11 @@ public class PVASettings
250250

251251

252252

253-
/** Whether to allow PVA to use IPv6
253+
/** Whether to allow PVA to use IPv6
254254
*
255-
* <p> If this is false then PVA will not attempt to
255+
* <p> If this is false then PVA will not attempt to
256256
* use any IPv6 capability at all. This is useful if your
257-
* system does not have any IPv6 support.
257+
* system does not have any IPv6 support.
258258
*/
259259
public static boolean EPICS_PVA_ENABLE_IPV6 = true;
260260

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ private void search(final Collection<SearchRequest.Channel> channels)
482482
// Use 'any' reply address since reply will be via this TCP socket
483483
final InetSocketAddress response_address = new InetSocketAddress(0);
484484

485-
SearchRequest.encode(true, seq, channels, response_address, tls , buffer);
485+
SearchRequest.encode(true, true, seq, channels, response_address, tls , buffer);
486486
};
487487
tcp.submit(search_request);
488488
}
@@ -517,7 +517,7 @@ private void sendSearch(final int seq, final Collection<SearchRequest.Channel> c
517517
{
518518
send_buffer.clear();
519519
final InetSocketAddress response = udp.getResponseAddress(addr);
520-
SearchRequest.encode(true, seq, channels, response, tls, send_buffer);
520+
SearchRequest.encode(true, true, seq, channels, response, tls, send_buffer);
521521
send_buffer.flip();
522522
try
523523
{
@@ -535,7 +535,7 @@ private void sendSearch(final int seq, final Collection<SearchRequest.Channel> c
535535
{
536536
send_buffer.clear();
537537
final InetSocketAddress response = udp.getResponseAddress(addr);
538-
SearchRequest.encode(false, seq, channels, response, tls, send_buffer);
538+
SearchRequest.encode(false, true, seq, channels, response, tls, send_buffer);
539539
send_buffer.flip();
540540
try
541541
{

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

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,6 @@ class ClientTCPHandler extends TCPHandler
6464
/** Client context */
6565
private final PVAClient client;
6666

67-
/** When using TLS, the socket may come with a local certificate
68-
* that TLS uses to authenticate to the server,
69-
* and this is the name from that certificate.
70-
* Otherwise <code>null</code>
71-
*/
72-
private String x509_name;
73-
7467
/** Channels that use this connection */
7568
private final CopyOnWriteArrayList<PVAChannel> channels = new CopyOnWriteArrayList<>();
7669

@@ -145,9 +138,6 @@ protected boolean initializeSocket()
145138
return false;
146139
}
147140

148-
// For TLS, check if the socket has a name that's used to authenticate
149-
x509_name = tls ? SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getLocalPrincipal()) : null;
150-
151141
// For default EPICS_CA_CONN_TMO: 30 sec, send echo at ~15 sec:
152142
// Check every ~3 seconds
153143
last_life_sign = last_message_sent = System.currentTimeMillis();
@@ -170,10 +160,30 @@ PVAClient getClient()
170160
return client;
171161
}
172162

173-
/** @return Name used by TLS socket's certificate, or <code>null</code> */
174-
String getX509Name()
163+
/** When using TLS, the socket has a peer (server, IOC) certificate
164+
* @return Name from server's certificate, or <code>null</code>
165+
*/
166+
String getServerX509Name()
167+
{
168+
try
169+
{
170+
if (tls)
171+
return SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getPeerPrincipal());
172+
}
173+
catch (Exception ex)
174+
{
175+
logger.log(Level.WARNING, "Cannot get server principal", ex);
176+
}
177+
return null;
178+
}
179+
180+
/** When using TLS, the socket may come with a local (client) certificate
181+
* that TLS uses to authenticate to the server.
182+
* @return Name from client's certificate, or <code>null</code> */
183+
String getClientX509Name()
175184
{
176-
return x509_name;
185+
return tls ? SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getLocalPrincipal())
186+
: null;
177187
}
178188

179189
/** @param channel Channel that uses this TCP connection */

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

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.epics.pva.PVASettings;
2323
import org.epics.pva.common.AddressInfo;
2424
import org.epics.pva.common.Network;
25+
import org.epics.pva.common.OriginTag;
2526
import org.epics.pva.common.PVAHeader;
2627
import org.epics.pva.common.SearchRequest;
2728
import org.epics.pva.common.SearchResponse;
@@ -87,7 +88,6 @@ public interface SearchResponseHandler
8788
// with the understanding that it will only receive broadcasts;
8889
// since they are often blocked by firewall, may receive nothing, ever.
8990
private final DatagramChannel udp_beacon;
90-
private final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
9191

9292
private volatile Thread search_thread4, search_thread6, beacon_thread;
9393

@@ -100,20 +100,22 @@ public ClientUDPHandler(final BeaconHandler beacon_handler,
100100
// IPv4 socket, also used to send broadcasts and for the local re-sending
101101
udp_search4 = Network.createUDP(StandardProtocolFamily.INET, null, 0);
102102
udp_search4.socket().setBroadcast(true);
103-
local_multicast = Network.configureLocalIPv4Multicast(udp_search4, PVASettings.EPICS_PVA_BROADCAST_PORT);
103+
local_multicast = Network.getLocalMulticastGroup(udp_search4, PVASettings.EPICS_PVA_BROADCAST_PORT);
104104
udp_localaddr4 = (InetSocketAddress) udp_search4.getLocalAddress();
105105

106106
String ipV6Msg;
107107

108108
// IPv6 sockets
109109
// Beacon socket only receives, does not send broadcasts
110-
if (PVASettings.EPICS_PVA_ENABLE_IPV6) {
110+
if (PVASettings.EPICS_PVA_ENABLE_IPV6)
111+
{
111112
udp_search6 = Network.createUDP(StandardProtocolFamily.INET6, null, 0);
112113
udp_localaddr6 = (InetSocketAddress) udp_search6.getLocalAddress();
113114
ipV6Msg = String.format(" and %s", udp_localaddr6);
114115
udp_beacon = Network.createUDP(StandardProtocolFamily.INET6, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
115116
}
116-
else {
117+
else
118+
{
117119
udp_search6 = null;
118120
udp_beacon = Network.createUDP(StandardProtocolFamily.INET, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
119121
udp_localaddr6 = null;
@@ -150,11 +152,8 @@ public void send(final ByteBuffer buffer, final AddressInfo info) throws Excepti
150152
}
151153
else
152154
{
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-
}
155+
if (!PVASettings.EPICS_PVA_ENABLE_IPV6)
156+
throw new Exception("EPICS_PVA_ENABLE_IPV6 must be enabled to use IPv6 address!");
158157

159158
synchronized (udp_search6)
160159
{
@@ -177,13 +176,15 @@ public void start()
177176
search_thread4.setDaemon(true);
178177
search_thread4.start();
179178

180-
if (PVASettings.EPICS_PVA_ENABLE_IPV6) {
179+
if (PVASettings.EPICS_PVA_ENABLE_IPV6)
180+
{
181181
final ByteBuffer receive_buffer6 = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
182182
search_thread6 = new Thread(() -> listen(udp_search6, receive_buffer6), "UDP6-receiver " + Network.getLocalAddress(udp_search6));
183183
search_thread6.setDaemon(true);
184184
search_thread6.start();
185185
}
186186

187+
final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
187188
beacon_thread = new Thread(() -> listen(udp_beacon, beacon_buffer), "UDP-beacon-receiver " + Network.getLocalAddress(udp_beacon));
188189
beacon_thread.setDaemon(true);
189190
beacon_thread.start();
@@ -197,6 +198,9 @@ protected boolean handleMessage(final InetSocketAddress from, final byte version
197198
{
198199
case PVAHeader.CMD_BEACON:
199200
return handleBeacon(from, version, payload, buffer);
201+
case PVAHeader.CMD_ORIGIN_TAG:
202+
// Will be decoded with CMD_SEARCH
203+
break;
200204
case PVAHeader.CMD_SEARCH:
201205
return handleSearchRequest(from, version, payload, buffer);
202206
case PVAHeader.CMD_SEARCH_RESPONSE:
@@ -290,7 +294,8 @@ private boolean handleBeacon(final InetSocketAddress from, final byte version,
290294
private boolean handleSearchRequest(final InetSocketAddress from, final byte version,
291295
final int payload, final ByteBuffer buffer)
292296
{
293-
final SearchRequest search = SearchRequest.decode(from, version, payload, buffer);
297+
final OriginTag origin = OriginTag.testForOriginOfSearch(from, buffer);
298+
final SearchRequest search = SearchRequest.decode(origin, from, version, payload, buffer);
294299
try
295300
{
296301
if (local_multicast != null && search != null && search.unicast)
@@ -300,7 +305,8 @@ private boolean handleSearchRequest(final InetSocketAddress from, final byte ver
300305
if (search.reply_required)
301306
{
302307
forward_buffer.clear();
303-
SearchRequest.encode(false, 0, null, search.client, search.tls, forward_buffer);
308+
OriginTag.encode(udp_search4, forward_buffer);
309+
SearchRequest.encode(false, search.reply_to_src_port, 0, null, search.client, search.tls, forward_buffer);
304310
forward_buffer.flip();
305311
logger.log(Level.FINER, () -> "Forward search to list servers to " + local_multicast + "\n" + Hexdump.toHexdump(forward_buffer));
306312
send(forward_buffer, local_multicast);
@@ -309,7 +315,8 @@ private boolean handleSearchRequest(final InetSocketAddress from, final byte ver
309315
else
310316
{
311317
forward_buffer.clear();
312-
SearchRequest.encode(false, search.seq, search.channels, search.client, search.tls, forward_buffer);
318+
OriginTag.encode(udp_search4, forward_buffer);
319+
SearchRequest.encode(false, search.reply_to_src_port, search.seq, search.channels, search.client, search.tls, forward_buffer);
313320
forward_buffer.flip();
314321
logger.log(Level.FINER, () -> "Forward search to " + local_multicast + "\n" + Hexdump.toHexdump(forward_buffer));
315322
send(forward_buffer, local_multicast);

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

Lines changed: 9 additions & 1 deletion
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
@@ -85,6 +85,9 @@ private static void info(final List<String> names) throws Exception
8585
final PVAChannel pv = iter.next();
8686
if (pv.getState() == ClientChannelState.CONNECTED)
8787
{
88+
PVASettings.logger.log(Level.INFO, "Server: " + pv.getTCP().getServerX509Name());
89+
PVASettings.logger.log(Level.INFO, "Client: " + pv.getTCP().getClientX509Name());
90+
8891
final PVAData data = pv.info(request).get(timeout_ms, TimeUnit.MILLISECONDS);
8992
System.out.println(pv.getName() + " = " + data.formatType());
9093
pv.close();
@@ -127,6 +130,9 @@ private static void get(final List<String> names) throws Exception
127130
final PVAChannel pv = iter.next();
128131
if (pv.getState() == ClientChannelState.CONNECTED)
129132
{
133+
PVASettings.logger.log(Level.INFO, "Server: " + pv.getTCP().getServerX509Name());
134+
PVASettings.logger.log(Level.INFO, "Client: " + pv.getTCP().getClientX509Name());
135+
130136
final PVAData data = pv.read(request).get(timeout_ms, TimeUnit.MILLISECONDS);
131137
System.out.println(pv.getName() + " = " + data);
132138
pv.close();
@@ -170,6 +176,8 @@ private static void monitor(final List<String> names) throws Exception
170176
{
171177
try
172178
{
179+
PVASettings.logger.log(Level.INFO, "Server: " + ch.getTCP().getServerX509Name());
180+
PVASettings.logger.log(Level.INFO, "Client: " + ch.getTCP().getClientX509Name());
173181
ch.subscribe(request, listener);
174182
}
175183
catch (Exception ex)

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

Lines changed: 2 additions & 2 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
@@ -52,7 +52,7 @@ public void handleCommand(final ClientTCPHandler tcp, final ByteBuffer buffer) t
5252
// Support "x509" or "ca" authorization, fall back to any-no-mouse
5353
final ClientAuthentication authentication;
5454
// Even if server suggests x509, check that we have a certificate with name
55-
if (tcp.getX509Name() != null && auth.contains(PVAAuth.X509))
55+
if (tcp.getClientX509Name() != null && auth.contains(PVAAuth.X509))
5656
authentication = ClientAuthentication.X509;
5757
else if (auth.contains(PVAAuth.CA))
5858
authentication = ClientAuthentication.CA;

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

Lines changed: 7 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
@@ -278,16 +278,16 @@ else if (family == StandardProtocolFamily.INET)
278278
return udp;
279279
}
280280

281-
/** Configure IPv4 socket to receive local multicast messages
281+
/** Get a local multicast group address for IPv4 socket
282282
*
283283
* IPv4 unicasts are re-sent as local multicast,
284284
* and this configures a socket to receive them
285285
*
286-
* @param udp UDP channel that should listen to multicast messages
286+
* @param udp UDP channel from which we plan to send multicast messages
287287
* @param port Port to use
288288
* @return Local multicast address, or <code>null</code> if no multicast support
289289
*/
290-
public static AddressInfo configureLocalIPv4Multicast(final DatagramChannel udp, final int port)
290+
public static AddressInfo getLocalMulticastGroup(final DatagramChannel udp, final int port)
291291
{
292292
try
293293
{
@@ -301,12 +301,12 @@ public static AddressInfo configureLocalIPv4Multicast(final DatagramChannel udp,
301301
{
302302
final InetAddress group = InetAddress.getByName(PVASettings.EPICS_PVA_MULTICAST_GROUP);
303303
final InetSocketAddress local_multicast = new InetSocketAddress(group, port);
304-
udp.join(group, loopback);
305-
306304
logger.log(Level.CONFIG, "Local multicast of IPv4 unicast using group " + local_multicast + " using network interface " + loopback.getDisplayName());
305+
306+
udp.join(group, loopback);
307+
// Default is TRUE anyway?
307308
udp.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true);
308309
udp.setOption(StandardSocketOptions.IP_MULTICAST_IF, loopback);
309-
310310
return new AddressInfo(false, local_multicast, 1, loopback);
311311
}
312312
}

0 commit comments

Comments
 (0)