Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/pva/TLS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ This is an example recipe for getting started.
Note its "Certificate identifier":

```
$ authnstd --name ioc --cert-usage hybrid
$ authnstd --name ioc --cert-usage ioc
Keychain file created : /home/user/.config/pva/1.3/server.p12
Certificate identifier : e53ed409:15273288300286014953
```
Expand Down
10 changes: 5 additions & 5 deletions core/pva/src/main/java/org/epics/pva/PVASettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,12 @@ public class PVASettings
* <p>Next, multicast groups may be added.
* Each multicast group must include an interface.
* <pre>
* 224.0.1.1,1@127.0.0.1 - Listen to local IPv4 multicasts
* 224.0.0.128,1@127.0.0.1 - Listen to local IPv4 multicasts
* [ff02::42:1],1@::1 - Listen to local IPv6 multicasts
* [ff02::42:1],1@en1 - Listen to IPv6 multicasts on network interface en1
* </pre>
*/
public static String EPICS_PVAS_INTF_ADDR_LIST = "0.0.0.0 [::] 224.0.1.1,1@127.0.0.1 [ff02::42:1],1@::1";
public static String EPICS_PVAS_INTF_ADDR_LIST = "0.0.0.0 [::] 224.0.0.128,1@127.0.0.1 [ff02::42:1],1@::1";

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



/** Whether to allow PVA to use IPv6
/** Whether to allow PVA to use IPv6
*
* <p> If this is false then PVA will not attempt to
* <p> If this is false then PVA will not attempt to
* use any IPv6 capability at all. This is useful if your
* system does not have any IPv6 support.
* system does not have any IPv6 support.
*/
public static boolean EPICS_PVA_ENABLE_IPV6 = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ private void search(final Collection<SearchRequest.Channel> channels)
// Use 'any' reply address since reply will be via this TCP socket
final InetSocketAddress response_address = new InetSocketAddress(0);

SearchRequest.encode(true, seq, channels, response_address, tls , buffer);
SearchRequest.encode(true, true, seq, channels, response_address, tls , buffer);
};
tcp.submit(search_request);
}
Expand Down Expand Up @@ -517,7 +517,7 @@ private void sendSearch(final int seq, final Collection<SearchRequest.Channel> c
{
send_buffer.clear();
final InetSocketAddress response = udp.getResponseAddress(addr);
SearchRequest.encode(true, seq, channels, response, tls, send_buffer);
SearchRequest.encode(true, true, seq, channels, response, tls, send_buffer);
send_buffer.flip();
try
{
Expand All @@ -535,7 +535,7 @@ private void sendSearch(final int seq, final Collection<SearchRequest.Channel> c
{
send_buffer.clear();
final InetSocketAddress response = udp.getResponseAddress(addr);
SearchRequest.encode(false, seq, channels, response, tls, send_buffer);
SearchRequest.encode(false, true, seq, channels, response, tls, send_buffer);
send_buffer.flip();
try
{
Expand Down
36 changes: 23 additions & 13 deletions core/pva/src/main/java/org/epics/pva/client/ClientTCPHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ class ClientTCPHandler extends TCPHandler
/** Client context */
private final PVAClient client;

/** When using TLS, the socket may come with a local certificate
* that TLS uses to authenticate to the server,
* and this is the name from that certificate.
* Otherwise <code>null</code>
*/
private String x509_name;

/** Channels that use this connection */
private final CopyOnWriteArrayList<PVAChannel> channels = new CopyOnWriteArrayList<>();

Expand Down Expand Up @@ -145,9 +138,6 @@ protected boolean initializeSocket()
return false;
}

// For TLS, check if the socket has a name that's used to authenticate
x509_name = tls ? SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getLocalPrincipal()) : null;

// For default EPICS_CA_CONN_TMO: 30 sec, send echo at ~15 sec:
// Check every ~3 seconds
last_life_sign = last_message_sent = System.currentTimeMillis();
Expand All @@ -170,10 +160,30 @@ PVAClient getClient()
return client;
}

/** @return Name used by TLS socket's certificate, or <code>null</code> */
String getX509Name()
/** When using TLS, the socket has a peer (server, IOC) certificate
* @return Name from server's certificate, or <code>null</code>
*/
String getServerX509Name()
{
try
{
if (tls)
return SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getPeerPrincipal());
}
catch (Exception ex)
{
logger.log(Level.WARNING, "Cannot get server principal", ex);
}
return null;
}

/** When using TLS, the socket may come with a local (client) certificate
* that TLS uses to authenticate to the server.
* @return Name from client's certificate, or <code>null</code> */
String getClientX509Name()
{
return x509_name;
return tls ? SecureSockets.getPrincipalCN(((SSLSocket) socket).getSession().getLocalPrincipal())
: null;
}

/** @param channel Channel that uses this TCP connection */
Expand Down
33 changes: 20 additions & 13 deletions core/pva/src/main/java/org/epics/pva/client/ClientUDPHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.epics.pva.PVASettings;
import org.epics.pva.common.AddressInfo;
import org.epics.pva.common.Network;
import org.epics.pva.common.OriginTag;
import org.epics.pva.common.PVAHeader;
import org.epics.pva.common.SearchRequest;
import org.epics.pva.common.SearchResponse;
Expand Down Expand Up @@ -87,7 +88,6 @@ public interface SearchResponseHandler
// with the understanding that it will only receive broadcasts;
// since they are often blocked by firewall, may receive nothing, ever.
private final DatagramChannel udp_beacon;
private final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);

private volatile Thread search_thread4, search_thread6, beacon_thread;

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

String ipV6Msg;

// IPv6 sockets
// Beacon socket only receives, does not send broadcasts
if (PVASettings.EPICS_PVA_ENABLE_IPV6) {
if (PVASettings.EPICS_PVA_ENABLE_IPV6)
{
udp_search6 = Network.createUDP(StandardProtocolFamily.INET6, null, 0);
udp_localaddr6 = (InetSocketAddress) udp_search6.getLocalAddress();
ipV6Msg = String.format(" and %s", udp_localaddr6);
udp_beacon = Network.createUDP(StandardProtocolFamily.INET6, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
}
else {
else
{
udp_search6 = null;
udp_beacon = Network.createUDP(StandardProtocolFamily.INET, null, PVASettings.EPICS_PVA_BROADCAST_PORT);
udp_localaddr6 = null;
Expand Down Expand Up @@ -150,11 +152,8 @@ public void send(final ByteBuffer buffer, final AddressInfo info) throws Excepti
}
else
{
if (!PVASettings.EPICS_PVA_ENABLE_IPV6) {
throw new Exception(
"EPICS_PVA_ENABLE_IPV6 must be enabled to use IPv6 address!"
);
}
if (!PVASettings.EPICS_PVA_ENABLE_IPV6)
throw new Exception("EPICS_PVA_ENABLE_IPV6 must be enabled to use IPv6 address!");

synchronized (udp_search6)
{
Expand All @@ -177,13 +176,15 @@ public void start()
search_thread4.setDaemon(true);
search_thread4.start();

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

final ByteBuffer beacon_buffer = ByteBuffer.allocate(PVASettings.MAX_UDP_PACKET);
beacon_thread = new Thread(() -> listen(udp_beacon, beacon_buffer), "UDP-beacon-receiver " + Network.getLocalAddress(udp_beacon));
beacon_thread.setDaemon(true);
beacon_thread.start();
Expand All @@ -197,6 +198,9 @@ protected boolean handleMessage(final InetSocketAddress from, final byte version
{
case PVAHeader.CMD_BEACON:
return handleBeacon(from, version, payload, buffer);
case PVAHeader.CMD_ORIGIN_TAG:
// Will be decoded with CMD_SEARCH
break;
case PVAHeader.CMD_SEARCH:
return handleSearchRequest(from, version, payload, buffer);
case PVAHeader.CMD_SEARCH_RESPONSE:
Expand Down Expand Up @@ -290,7 +294,8 @@ private boolean handleBeacon(final InetSocketAddress from, final byte version,
private boolean handleSearchRequest(final InetSocketAddress from, final byte version,
final int payload, final ByteBuffer buffer)
{
final SearchRequest search = SearchRequest.decode(from, version, payload, buffer);
final OriginTag origin = OriginTag.testForOriginOfSearch(from, buffer);
final SearchRequest search = SearchRequest.decode(origin, from, version, payload, buffer);
try
{
if (local_multicast != null && search != null && search.unicast)
Expand All @@ -300,7 +305,8 @@ private boolean handleSearchRequest(final InetSocketAddress from, final byte ver
if (search.reply_required)
{
forward_buffer.clear();
SearchRequest.encode(false, 0, null, search.client, search.tls, forward_buffer);
OriginTag.encode(udp_search4, forward_buffer);
SearchRequest.encode(false, search.reply_to_src_port, 0, null, search.client, search.tls, forward_buffer);
forward_buffer.flip();
logger.log(Level.FINER, () -> "Forward search to list servers to " + local_multicast + "\n" + Hexdump.toHexdump(forward_buffer));
send(forward_buffer, local_multicast);
Expand All @@ -309,7 +315,8 @@ private boolean handleSearchRequest(final InetSocketAddress from, final byte ver
else
{
forward_buffer.clear();
SearchRequest.encode(false, search.seq, search.channels, search.client, search.tls, forward_buffer);
OriginTag.encode(udp_search4, forward_buffer);
SearchRequest.encode(false, search.reply_to_src_port, search.seq, search.channels, search.client, search.tls, forward_buffer);
forward_buffer.flip();
logger.log(Level.FINER, () -> "Forward search to " + local_multicast + "\n" + Hexdump.toHexdump(forward_buffer));
send(forward_buffer, local_multicast);
Expand Down
10 changes: 9 additions & 1 deletion core/pva/src/main/java/org/epics/pva/client/PVAClientMain.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -85,6 +85,9 @@ private static void info(final List<String> names) throws Exception
final PVAChannel pv = iter.next();
if (pv.getState() == ClientChannelState.CONNECTED)
{
PVASettings.logger.log(Level.INFO, "Server: " + pv.getTCP().getServerX509Name());
PVASettings.logger.log(Level.INFO, "Client: " + pv.getTCP().getClientX509Name());

final PVAData data = pv.info(request).get(timeout_ms, TimeUnit.MILLISECONDS);
System.out.println(pv.getName() + " = " + data.formatType());
pv.close();
Expand Down Expand Up @@ -127,6 +130,9 @@ private static void get(final List<String> names) throws Exception
final PVAChannel pv = iter.next();
if (pv.getState() == ClientChannelState.CONNECTED)
{
PVASettings.logger.log(Level.INFO, "Server: " + pv.getTCP().getServerX509Name());
PVASettings.logger.log(Level.INFO, "Client: " + pv.getTCP().getClientX509Name());

final PVAData data = pv.read(request).get(timeout_ms, TimeUnit.MILLISECONDS);
System.out.println(pv.getName() + " = " + data);
pv.close();
Expand Down Expand Up @@ -170,6 +176,8 @@ private static void monitor(final List<String> names) throws Exception
{
try
{
PVASettings.logger.log(Level.INFO, "Server: " + ch.getTCP().getServerX509Name());
PVASettings.logger.log(Level.INFO, "Client: " + ch.getTCP().getClientX509Name());
ch.subscribe(request, listener);
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -52,7 +52,7 @@ public void handleCommand(final ClientTCPHandler tcp, final ByteBuffer buffer) t
// Support "x509" or "ca" authorization, fall back to any-no-mouse
final ClientAuthentication authentication;
// Even if server suggests x509, check that we have a certificate with name
if (tcp.getX509Name() != null && auth.contains(PVAAuth.X509))
if (tcp.getClientX509Name() != null && auth.contains(PVAAuth.X509))
authentication = ClientAuthentication.X509;
else if (auth.contains(PVAAuth.CA))
authentication = ClientAuthentication.CA;
Expand Down
14 changes: 7 additions & 7 deletions core/pva/src/main/java/org/epics/pva/common/Network.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019-2023 Oak Ridge National Laboratory.
* Copyright (c) 2019-2025 Oak Ridge National Laboratory.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -278,16 +278,16 @@ else if (family == StandardProtocolFamily.INET)
return udp;
}

/** Configure IPv4 socket to receive local multicast messages
/** Get a local multicast group address for IPv4 socket
*
* IPv4 unicasts are re-sent as local multicast,
* and this configures a socket to receive them
*
* @param udp UDP channel that should listen to multicast messages
* @param udp UDP channel from which we plan to send multicast messages
* @param port Port to use
* @return Local multicast address, or <code>null</code> if no multicast support
*/
public static AddressInfo configureLocalIPv4Multicast(final DatagramChannel udp, final int port)
public static AddressInfo getLocalMulticastGroup(final DatagramChannel udp, final int port)
{
try
{
Expand All @@ -301,12 +301,12 @@ public static AddressInfo configureLocalIPv4Multicast(final DatagramChannel udp,
{
final InetAddress group = InetAddress.getByName(PVASettings.EPICS_PVA_MULTICAST_GROUP);
final InetSocketAddress local_multicast = new InetSocketAddress(group, port);
udp.join(group, loopback);

logger.log(Level.CONFIG, "Local multicast of IPv4 unicast using group " + local_multicast + " using network interface " + loopback.getDisplayName());

udp.join(group, loopback);
// Default is TRUE anyway?
udp.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true);
udp.setOption(StandardSocketOptions.IP_MULTICAST_IF, loopback);

return new AddressInfo(false, local_multicast, 1, loopback);
}
}
Expand Down
Loading