Skip to content

Commit

Permalink
Merge remote-tracking branch 'mdavid/sendto-pktinfo' into test_pktinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
FreddieAkeroyd committed Feb 18, 2025
2 parents 49f7dca + 9c3f4fa commit 5e056eb
Show file tree
Hide file tree
Showing 22 changed files with 380 additions and 67 deletions.
2 changes: 2 additions & 0 deletions abi-diff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ setupsrc() {
#make -C "$2/bundle" libevent -j8
make -C "$2" CROSS_COMPILER_TARGET_ARCHS= OPT_CFLAGS='-g -Og' OPT_CXXFLAGS='-g -Og' ioc -j8

install -C -d compat_reports/libpvxs
install -C -d compat_reports/libpvxsIoc
nm -g "$2"/lib/linux-*/libpvxs.so.* |sed -e 's|^[0-9a-f]*\s*||' > "compat_reports/libpvxs/$1.nm"

abi-dumper "$2"/lib/linux-*/libpvxs.so.* -o "compat_reports/libpvxs/$1.dump" -public-headers "$2/include" -lver "$1"
Expand Down
5 changes: 3 additions & 2 deletions configure/CONFIG_PVXS_VERSION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PVXS_MAJOR_VERSION = 1
PVXS_MINOR_VERSION = 3
PVXS_MAINTENANCE_VERSION = 2
PVXS_MAINTENANCE_VERSION = 3

# Version range conditions in Makefiles
#
Expand All @@ -12,7 +12,8 @@ PVXS_MAINTENANCE_VERSION = 2
#
# ifneq ($(PVXS_X_Y_Z),YES) # PVXS != X.Y.Z
#
PVXS_1_3_2 = YES
PVXS_1_3_3 = YES
PVXS_1_3_2 = NO
PVXS_1_3_1 = NO
PVXS_1_3_0 = NO
PVXS_1_2_4 = NO
Expand Down
16 changes: 16 additions & 0 deletions documentation/ioc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ Currently supported format hints are:
- Exponential
- Engineering

When subscribing, the client provided ``pvRequest`` Value may contain the field
``records._options.DBE`` with a string or unsigned integer.
This value is mapped to a DataBase Event (DBE) bit mask.

``DBE`` may be either a string or an unsigned integer.
Use of an integer is recommended.
The ``DBE_*`` macros from the ``caeventmask.h`` header may be used
(``DBE_VALUE=1``, ``DBE_ARCHIVE=2``, ``DBE_ALARM=4``).
``DBE_PROPERTY`` will be ignored if given as this event is always
handled specially.
Alternately, ``DBE`` may be a string using the old caProvider "parsing"
rules. This is not recommended.

Until UNRELEASED ``DBE_ARCHIVE`` was not handled correctly.


Group PV
^^^^^^^^

Expand Down
3 changes: 2 additions & 1 deletion documentation/releasenotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ Release Notes

* Client: search retry step reset on channel reconnection (Anze Zagar)
* Various documentation improvements! (Érico Nogueira)
* Fix dbLoadGroups (Érico Nogueira)
* Fix build with epics-base 7.0.7 (Rémi NICOLE)
* ioc: Fix dbLoadGroups (Érico Nogueira)
* ioc: Fix handling of ``DBE_ARCHIVE`` when requested via ``records._options.DBE``

1.3.2 (Oct 2024)
------------------
Expand Down
10 changes: 7 additions & 3 deletions ioc/singlesource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,18 @@ void subscriptionValueCallback(void* userArg, struct dbChannel* pChannel,
int, struct db_field_log* pDbFieldLog) noexcept {
auto subscriptionContext = (SingleSourceSubscriptionCtx*)userArg;
subscriptionContext->hadValueEvent = true;
auto change = UpdateType::type(UpdateType::Value | UpdateType::Alarm);
auto change = subscriptionContext->pValueEventSubscription.mask;
#if EPICS_VERSION_INT >= VERSION_INT(7, 0, 6, 0)
if(pDbFieldLog) {
// when available, use DBE mask from db_field_log
change = UpdateType::type(pDbFieldLog->mask & UpdateType::Everything);
change = pDbFieldLog->mask;
}
#endif
subscriptionCallback(subscriptionContext, change, pChannel, pDbFieldLog);
// ARCHIVE events will get the same data fields as VALUE
if(change & DBE_ARCHIVE)
change = (change&~DBE_ARCHIVE)|DBE_VALUE;
change &= UpdateType::Everything; // does not include DBE_ARCHIVE
subscriptionCallback(subscriptionContext, UpdateType::type(change), pChannel, pDbFieldLog);
}

void subscriptionPropertiesCallback(void* userArg, struct dbChannel* pChannel, int,
Expand Down
2 changes: 2 additions & 0 deletions ioc/subscriptionctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace ioc {
class Subscription {
std::shared_ptr<std::remove_pointer<dbEventSubscription>::type> sub; // holds void* returned by db_add_event()
public:
unsigned mask=0;
/* Add a subscription event by calling db_add_event using the given subscriptionCtx
* and selecting the correct elements based on the given type of event being added.
* You need to specify the correct options that correspond to the event type.
Expand All @@ -45,6 +46,7 @@ class Subscription {
});
if(!sub)
throw std::runtime_error("Failed to create db subscription");
mask = select;
}
void cancel() {
sub.reset();
Expand Down
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools_dso>=2.7a1", "setuptools_dso>=2.11a2; python_version >= '3.12'", "epicscorelibs>=7.0.7.99.1.1a2"]
requires = [
"setuptools",
"wheel",
"setuptools_dso>=2.7a1",
"setuptools_dso>=2.11a2; python_version >= '3.12'",
"epicscorelibs>=7.0.7.99.1.2a1",
]
10 changes: 10 additions & 0 deletions python/pvxslibs/ioc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

from epicscorelibs import ioc
import pvxslibs.path

if __name__ == "__main__":
os.environ.setdefault("PVXS_QSRV_ENABLE", "YES")
pvxs_dbd_load = (("pvxsIoc.dbd", pvxslibs.path.dbd_path), )
pvxs_dso_load = ("pvxslibs.lib.pvxsIoc", )
ioc.main(extra_dbd_load=pvxs_dbd_load, extra_dso_load=pvxs_dso_load)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ def define_DSOS(self):


pvxs_ver = '%(PVXS_MAJOR_VERSION)s.%(PVXS_MINOR_VERSION)s.%(PVXS_MAINTENANCE_VERSION)s'%pvxsversion
#pvxs_ver += 'a2'
pvxs_ver += 'a1'

with open(os.path.join(os.path.dirname(__file__), 'README.md')) as F:
long_description = F.read()
Expand Down
3 changes: 1 addition & 2 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,7 @@ Value buildCAMethod()
}

ContextImpl::ContextImpl(const Config& conf, const evbase& tcp_loop)
:ifmap(IfaceMap::instance())
,effective([conf]() -> Config{
:effective([conf]() -> Config{
Config eff(conf);
eff.expand();
return eff;
Expand Down
1 change: 0 additions & 1 deletion src/clientimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ struct Discovery final : public OperationBase
struct ContextImpl : public std::enable_shared_from_this<ContextImpl>
{
SockAttach attach;
IfaceMap& ifmap;

enum state_t {
Init,
Expand Down
28 changes: 28 additions & 0 deletions src/evhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,21 @@ uint64_t IfaceMap::index_of(const std::string& name)
return ret;
}

uint64_t IfaceMap::index_of(const SockAddr &addr)
{
Guard G(lock);

uint64_t ret = 0u;
try_cache(*this, [&ret, this, addr]() {
auto it = byAddr.find(addr);
bool hit = it!=byAddr.end() && !it->second.second;
if(hit)
ret = it->second.first->index;
return hit;
});
return ret;
}

bool IfaceMap::is_iface(const SockAddr& addr)
{
Guard G(lock);
Expand All @@ -847,6 +862,19 @@ bool IfaceMap::is_iface(const SockAddr& addr)
});
}

bool IfaceMap::is_lo(uint64_t index)
{
bool is_lo = false;
(void)try_cache(*this, [this, &is_lo, index]() {
auto ifit(byIndex.find(index));
if(ifit!=byIndex.end()) { // hit
is_lo = ifit->second.isLO;
}
return false;
});
return is_lo;
}

bool IfaceMap::is_broadcast(const SockAddr& addr)
{
Guard G(lock);
Expand Down
4 changes: 4 additions & 0 deletions src/evhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,12 @@ struct PVXS_API IfaceMap {
std::string name_of(const SockAddr& addr);
// returns 0 if not found
uint64_t index_of(const std::string& name);
// lookup interface index by interface address (not broadcast addr)
uint64_t index_of(const SockAddr& addr);
// is this a valid interface or broadcast address?
bool is_iface(const SockAddr& addr);
// is this index the/a loopback interface?
bool is_lo(uint64_t index);
// is this a valid interface or broadcast address?
bool is_broadcast(const SockAddr& addr);
// look up interface address. useful for IPV4. returns AF_UNSPEC if not found
Expand Down
71 changes: 68 additions & 3 deletions src/os/WIN32/osdSockExt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,36 @@ DEFINE_LOGGER(logiface, "pvxs.iface");

static
LPFN_WSARECVMSG WSARecvMsg;
static
LPFN_WSASENDMSG WSASendMsg;

static
void oseDoOnce()
{
evsocket dummy(AF_INET, SOCK_DGRAM, 0);
GUID guid = WSAID_WSARECVMSG;
GUID guid;
DWORD nout;

guid = WSAID_WSARECVMSG;
if(WSAIoctl(dummy.sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&WSARecvMsg, sizeof(WSARecvMsg),
&nout, nullptr, nullptr))
{
cantProceed("Unable to get &WSARecvMsg: %d", WSAGetLastError());
}
if(!WSARecvMsg)
cantProceed("Unable to get &WSARecvMsg!!");

guid = WSAID_WSASENDMSG;
if(WSAIoctl(dummy.sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
&guid, sizeof(guid),
&WSASendMsg, sizeof(WSASendMsg),
&nout, nullptr, nullptr))
{
cantProceed("Unable to get &WSASendMsg: %d", WSAGetLastError());
}

if(!WSARecvMsg || !WSASendMsg)
cantProceed("Unable to get &WSARecvMsg or &WSASendMsg");

evsocket::canIPv6 = evsocket::init_canIPv6();
evsocket::ipstack = is_wine() ? evsocket::Linsock : evsocket::Winsock;
Expand Down Expand Up @@ -134,6 +147,58 @@ int recvfromx::call()
}
}

int sendtox::call()
{
WSAMSG msg{};

WSABUF iov = {(ULONG)buflen, (char*)buf};
msg.lpBuffers = &iov;
msg.dwBufferCount = 1u;

msg.name = const_cast<sockaddr*>(&(*dst)->sa);
msg.namelen = dst->size();

alignas (WSACMSGHDR) char cbuf[
// only need space for IPv4 option(s) or IPv6 option, never both.
impl::cmax(WSA_CMSG_SPACE(sizeof(in_pktinfo)), WSA_CMSG_SPACE(sizeof(in6_pktinfo)))
] = {};

if(srcif && src && src->family()==AF_INET) {
auto cmsg(reinterpret_cast<WSACMSGHDR*>(cbuf));
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
auto info(reinterpret_cast<in_pktinfo*>(WSA_CMSG_DATA(cmsg)));
memset(info, 0, sizeof(*info));
info->ipi_ifindex = srcif;
if(src) {
info->ipi_addr = (*src)->in.sin_addr; // source in IP header
}
msg.Control.buf = cbuf;
msg.Control.len = WSA_CMSG_SPACE(sizeof(in_pktinfo));

} else if(srcif && src && src->family()==AF_INET6) {
auto cmsg(reinterpret_cast<WSACMSGHDR*>(cbuf));
cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(in6_pktinfo));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
auto info(reinterpret_cast<in6_pktinfo*>(WSA_CMSG_DATA(cmsg)));
memset(info, 0, sizeof(*info));
info->ipi6_ifindex = srcif;
if(src) {
info->ipi6_addr = (*src)->in6.sin6_addr; // source in IP header
}
msg.Control.buf = cbuf;
msg.Control.len = WSA_CMSG_SPACE(sizeof(in6_pktinfo));
// real winsock insists that Control.len includes padding
}

DWORD nSent = 0;
if(WSASendMsg(sock, &msg, 0, &nSent, nullptr, nullptr))
return -1;
return nSent;
}

namespace impl {

#ifndef GAA_FLAG_INCLUDE_ALL_INTERFACES
Expand Down
54 changes: 54 additions & 0 deletions src/os/default/osdSockExt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,60 @@ int recvfromx::call()
return ret;
}

int sendtox::call()
{
msghdr msg{};

iovec iov = {const_cast<void*>(buf), buflen};
msg.msg_iov = &iov;
msg.msg_iovlen = 1u;

msg.msg_name = const_cast<sockaddr*>(&(*dst)->sa);
msg.msg_namelen = dst->size();

// only need space for IPv4 option(s) or IPv6 option, never both.
alignas (cmsghdr) char cbuf[impl::cmax(0
#ifdef IP_PKTINFO
+ CMSG_SPACE(sizeof(in_pktinfo))
#endif
, CMSG_SPACE(sizeof(in6_pktinfo)))
] = {};

#ifdef IP_PKTINFO
if(srcif && src && src->family()==AF_INET) {
auto cmsg(reinterpret_cast<cmsghdr*>(cbuf));
cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
auto info(reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg)));
memset(info, 0, sizeof(*info));
info->ipi_ifindex = srcif;
if(src) {
info->ipi_addr = (*src)->in.sin_addr; // source in IP header
info->ipi_spec_dst = (*src)->in.sin_addr; // source use in local routing table lookup
}
msg.msg_control = cbuf;
msg.msg_controllen = cmsg->cmsg_len;
} else
#endif
if(srcif && src && src->family()==AF_INET6) {
auto cmsg(reinterpret_cast<cmsghdr*>(cbuf));
cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
auto info(reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg)));
memset(info, 0, sizeof(*info));
info->ipi6_ifindex = srcif;
if(src) {
info->ipi6_addr = (*src)->in6.sin6_addr; // source in IP header
}
msg.msg_control = cbuf;
msg.msg_controllen = cmsg->cmsg_len;
}

return sendmsg(sock, &msg, 0);
}

namespace impl {

decltype (IfaceMap::byIndex) IfaceMap::_refresh() {
Expand Down
12 changes: 12 additions & 0 deletions src/osiSockExt.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,18 @@ struct recvfromx {
int call();
};

struct sendtox {
evutil_socket_t sock;
const void *buf;
size_t buflen;
const SockAddr* dst;
const SockAddr* src; // if !NULL override UDP source address
uint64_t srcif; // if !=0 override routing to send through this interface

PVXS_API
int call();
};

} // namespace pvxs

#endif // OSISOCKEXT_H
Loading

0 comments on commit 5e056eb

Please sign in to comment.