21 Commits
Author SHA1 Message Date
chgabriel79andGitHub 9dbbf54d1d Merge c13a0039db into 9f2896cfc0 2026-03-27 07:10:40 +00:00
Kyle Schwarz 9f2896cfc0 Update libicsneo 2026-03-24 10:24:36 -04:00
Kyle Schwarz 2838f4488c Update libicsneo 2025-10-15 12:00:31 -04:00
Kyle Schwarz 8c7f306771 Update libicsneo 2025-06-19 19:21:59 -04:00
Kyle Schwarz 945a027b5b Update libicsneo 2025-05-07 10:02:46 -04:00
Kyle Schwarz 1acc51af7d Update copyright year to 2025 2025-05-07 10:02:27 -04:00
Christian Gabriel c13a0039db 3.3.0
Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-09-25 10:41:16 +02:00
Christian Gabriel fe777df9ee Allow setting individual bit timing parameters
Rework of the bitrate setting to allow finer control about the
bit timing settings.

The current settings are propagated via existing netlink
interfaces, reporting the current settings uses custom
ioctls.

Also report bit errors and bus off state if sent from the
device.

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-09-25 10:41:16 +02:00
Christian GabrielandKyle Schwarz be51cce4f1 3.2.0
Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:13:32 -04:00
Christian GabrielandKyle Schwarz 8dbd767b3e Add support for bitrate setting
If kernel driver version is new enough, report the currently set
bitrates via ioctl.
Kernel will report changed bitrates through special read operations,
send these values to the device.

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:13:26 -04:00
Kyle Schwarz 52db71754d 3.1.2 2024-01-02 10:16:28 -05:00
Kyle Schwarz d611db1318 Update libicsneo 2024-01-02 10:16:13 -05:00
Kyle Schwarz 707892e9b7 Update copyright year to 2024 2024-01-02 10:10:20 -05:00
kschwarz-intrepidcsandGitHub 154c58792e Merge pull request #13 from j-rge/add_scan_interval_param
Add scan-interval-ms parameter
2023-09-27 20:35:20 -04:00
Jorge Alejandro 1d0cdb9f00 Add scan-interval-ms parameter
Allow user to specify the scan interval for searching for devices.
Scanning places every interface on the machine in promiscuous mode
and currently runs once a second.  This behaviour may have undesirable
side-effects and also pollutes the kernel log.

The new parameter --scan-interval-ms <interval> will allow the user
to specify the rate at which scanning occurs.  If equal to 0, then
only a single scan is performed.
2023-09-17 18:13:21 -04:00
Kyle Schwarz b56fc99c2c Update libicsneo 2023-07-11 22:44:19 -04:00
Kyle Schwarz 3e96cc9105 3.1.0
Update libicsneo & copyright
2023-05-05 00:26:52 -04:00
Kyle Schwarz eca0ec0c25 Bump project version to match module 2022-09-01 13:15:46 -04:00
Gary Chong b33ec54593 Added Functionality for Ethernet 2022-08-16 15:31:35 -04:00
Paul Hollinsky b5d56bc740 v2.1.0
Update copyright date
Update to libicsneo v0.3.0
Report transmits down to the kernel for echo
Allow filtering devices by serial number
2022-03-31 18:18:33 -04:00
Paul Hollinsky 9ba6f2cec5 Update copyright date to 2022 2022-03-31 18:13:23 -04:00
9 changed files with 686 additions and 73 deletions
+1
View File
@@ -1,3 +1,4 @@
[submodule "third-party/libicsneo"] [submodule "third-party/libicsneo"]
path = third-party/libicsneo path = third-party/libicsneo
url = https://github.com/intrepidcs/libicsneo.git url = https://github.com/intrepidcs/libicsneo.git
branch = master
+21
View File
@@ -1,3 +1,24 @@
v3.1.2
Update libicsneo
Update copyright
Add scan-interval-ms parameter (Jorge Alejandro <jorge.a.alejandro@gmail.com>)
v3.1.1
Update libicsneo
v3.1.0
Update libicsneo
Update copyright
v3.0.0
Added Functionality for Ethernet
v2.1.0
Update copyright date
Update to libicsneo v0.3.0
Report transmits down to the kernel for echo
Allow filtering devices by serial number
v2.0.3 v2.0.3
Update copyright date Update copyright date
Update to libicsneo v0.2.0 Update to libicsneo v0.2.0
+2 -2
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.2)
project(libicsneo-socketcan-daemon VERSION 2.1.0) project(libicsneo-socketcan-daemon VERSION 3.3.0)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
@@ -27,5 +27,5 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
add_subdirectory("third-party/libicsneo") add_subdirectory("third-party/libicsneo")
add_executable(libicsneo-socketcan-daemon src/main.cpp) add_executable(libicsneo-socketcan-daemon src/main.cpp src/netlink.c)
target_link_libraries(libicsneo-socketcan-daemon icsneocpp) target_link_libraries(libicsneo-socketcan-daemon icsneocpp)
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016-2021 Intrepid Control Systems, Inc. Copyright (c) 2016-2025 Intrepid Control Systems, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
+1 -1
View File
@@ -1,4 +1,4 @@
Version 2.0.3 Version 3.1.2
This is the usermode daemon for the Intrepid Control Systems SocketCAN support. This daemon requires that ```intrepid.ko``` is loaded on your system. This is the usermode daemon for the Intrepid Control Systems SocketCAN support. This daemon requires that ```intrepid.ko``` is loaded on your system.
+516 -37
View File
@@ -17,6 +17,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/can/netlink.h>
#include <icsneo/icsneocpp.h> #include <icsneo/icsneocpp.h>
#include <icsneo/communication/message/neomessage.h> #include <icsneo/communication/message/neomessage.h>
@@ -25,24 +26,33 @@
#include <icsneo/communication/message/callback/canmessagecallback.h> #include <icsneo/communication/message/callback/canmessagecallback.h>
#include <generated/buildinfo.h> #include <generated/buildinfo.h>
#include "netlink.h"
#define LOG(LVL, MSG) do{if(runningAsDaemon) syslog(LVL, MSG); \ #define LOG(LVL, MSG) do{if(runningAsDaemon) syslog(LVL, MSG); \
else fprintf(stderr, MSG);}while(0) else fprintf(stderr, MSG);}while(0)
#define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \ #define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \
else fprintf(stderr, MSG, __VA_ARGS__);}while(0) else fprintf(stderr, MSG, __VA_ARGS__);}while(0)
#define SIOCSADDIF 0x3001 #define SIOCSADDCANIF 0x3001
#define SIOCSREMOVEIF 0x3002 #define SIOCSADDETHIF 0x3002
#define SIOCGSHAREDMEMSIZE 0x3003 #define SIOCSREMOVECANIF 0x3003
#define SIOCSMSGSWRITTEN 0x3004 #define SIOCSREMOVEETHIF 0x3004
#define SIOCGMAXIFACES 0x3005 #define SIOCGSHAREDMEMSIZE 0x3005
#define SIOCGVERSION 0x3006 #define SIOCSMSGSWRITTEN 0x3006
#define SIOCGCLIENTVEROK 0x3007 #define SIOCGMAXIFACES 0x3007
#define SIOCGVERSION 0x3008
#define SIOCGCLIENTVEROK 0x3009
#define SIOCSBAUDRATE 0x300A
#define SIOCSERRCOUNT 0x300B
#define SIOCSIFSETTINGS 0x300C
#define RX_BOX_SIZE (sharedMemSize / (maxInterfaces * 2)) #define RX_BOX_SIZE (sharedMemSize / (maxInterfaces * 2))
#define TX_BOX_SIZE (sharedMemSize / 4) #define TX_BOX_SIZE (sharedMemSize / 4)
#define GET_RX_BOX(DEVICE_INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (RX_BOX_SIZE * DEVICE_INDEX)) #define GET_RX_BOX(DEVICE_INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (RX_BOX_SIZE * DEVICE_INDEX))
#define GET_TX_BOX(INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (sharedMemSize / 2) + (INDEX * TX_BOX_SIZE)) #define GET_TX_BOX(INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (sharedMemSize / 2) + (INDEX * TX_BOX_SIZE))
#define DEFAULT_SCAN_INTERVAL_MS 1000
bool runningAsDaemon = false; bool runningAsDaemon = false;
int driver = 0; // /dev/intrepid_netdevice int driver = 0; // /dev/intrepid_netdevice
int driverMajor = 0; int driverMajor = 0;
@@ -52,6 +62,7 @@ int maxInterfaces = 0; // From driver
int sharedMemSize = 0; // From driver int sharedMemSize = 0; // From driver
void* sharedMemory = nullptr; void* sharedMemory = nullptr;
std::string serialFilter; std::string serialFilter;
int scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS;
std::atomic<bool> stopRunning(false); std::atomic<bool> stopRunning(false);
@@ -61,41 +72,202 @@ struct intrepid_pending_tx_info {
size_t bytes; size_t bytes;
}; };
#define ICS_MAGIC 0x49435343 // ICSC
struct add_can_if_info {
char alias[IFALIASZ];
__u32 magic;
__u32 ctrl_mode;
struct can_clock clock;
struct can_bittiming_const bittiming_const;
struct can_bittiming_const data_bittiming_const;
};
struct can_err_report {
int device;
enum can_state state;
struct can_berr_counter err_count;
};
struct can_dev_settings {
int device;
struct can_bittiming bittiming;
struct can_bittiming data_bittiming;
__u32 ctrl_mode;
bool termination;
};
static struct can_clock clock_bxcan = {
.freq = 80000000,
};
static struct can_bittiming_const bittiming_const_bxcan = {
.name = "bxcan-31X",
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 256,
.tseg2_min = 2, /* Time segment 2 = phase_seg2 */
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1,
};
static struct can_bittiming_const data_bittiming_const_bxcan = {
.name = "bxcan-31X",
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 32,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 16,
.sjw_max = 16,
.brp_min = 1,
.brp_max = 32,
.brp_inc = 1,
};
static struct can_clock clock_dspic = {
.freq = 40000000,
};
static struct can_bittiming_const bittiming_const_dspic = {
.name = "dspic33fj",
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 8,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 8,
.sjw_max = 4,
.brp_min = 2,
.brp_max = 128,
.brp_inc = 1,
};
static struct can_dev_info {
devicetype_t device_type;
struct can_clock *clock;
struct can_bittiming_const *bittiming_const;
struct can_bittiming_const *data_bittiming_const;
} dev_infos[] = {
{ icsneo::DeviceType::Enum::ECU_AVB, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADMars, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::VCAN4_1, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADPluto, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::VCAN4_2EL, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::FIRE3, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADJupiter, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::VCAN4_IND, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADGigastar, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RED2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RAD_A2B, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADEpsilon, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADMoon3, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADComet, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::FIRE3_FlexRay, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::VCAN4_4, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::VCAN4_2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::FIRE2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADGalaxy, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::RADStar2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
{ icsneo::DeviceType::Enum::FIRE, &clock_dspic, &bittiming_const_dspic, NULL },
{ icsneo::DeviceType::Enum::VCAN3, &clock_dspic, &bittiming_const_dspic, NULL },
{ icsneo::DeviceType::Enum::RED, &clock_dspic, &bittiming_const_dspic, NULL },
};
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
static struct can_dev_info* get_infos_for_device(devicetype_t device_type)
{
for (size_t i = 0; i < ARRAY_SIZE(dev_infos); ++i) {
if (dev_infos[i].device_type == device_type) {
return &dev_infos[i];
}
}
return NULL;
}
class NetworkInterface { class NetworkInterface {
public: public:
NetworkInterface(const std::string& desiredName) : name(desiredName) { NetworkInterface(const std::string& desiredName, icsneo::Network::Type device, devicetype_t device_type)
char ifname[IFALIASZ + 1] = {0}; : type(device), name(desiredName) {
strncpy(ifname, name.c_str(), IFALIASZ); struct add_can_if_info info = {
kernelHandle = ioctl(driver, SIOCSADDIF, ifname); .magic = ICS_MAGIC,
};
strncpy(info.alias, name.c_str(), IFALIASZ);
if(device == icsneo::Network::Type::CAN) {
struct can_dev_info *dev_info = get_infos_for_device(device_type);
if (dev_info) {
info.ctrl_mode = CAN_CTRLMODE_BERR_REPORTING;
info.clock = *(dev_info->clock);
info.bittiming_const = *(dev_info->bittiming_const);
if (dev_info->data_bittiming_const) {
info.ctrl_mode |= CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO;
info.data_bittiming_const = *(dev_info->data_bittiming_const);
}
}
kernelHandle = ioctl(driver, SIOCSADDCANIF, &info); // this will call the intrepid_dev_ioctl()
} else if(device == icsneo::Network::Type::Ethernet) {
kernelHandle = ioctl(driver, SIOCSADDETHIF, &info.alias); // this will call the intrepid_dev_ioctl()
}
if(openedSuccessfully()) { if(openedSuccessfully()) {
ifindex = ioctl(driver, SIOCGIFINDEX, kernelHandle);
LOGF(LOG_INFO, "Ifindex for device %s is %d\n", name.c_str(), ifindex);
rxBox = GET_RX_BOX(kernelHandle); rxBox = GET_RX_BOX(kernelHandle);
rxBoxCurrentPosition = rxBox; rxBoxCurrentPosition = rxBox;
} }
} }
~NetworkInterface() { ~NetworkInterface() {
if(openedSuccessfully()) { if(openedSuccessfully()) {
int res = 0;
LOGF(LOG_DEBUG, "Removing device %s with handle %d\n", name.c_str(), kernelHandle); LOGF(LOG_DEBUG, "Removing device %s with handle %d\n", name.c_str(), kernelHandle);
int res = ioctl(driver, SIOCSREMOVEIF, kernelHandle); if(type == icsneo::Network::Type::CAN) {
res = ioctl(driver, SIOCSREMOVECANIF, kernelHandle);
} else if(type == icsneo::Network::Type::Ethernet) {
res = ioctl(driver, SIOCSREMOVEETHIF, kernelHandle);
}
LOGF(LOG_DEBUG, "Removed device %s with handle %d, result %d\n", name.c_str(), kernelHandle, res); LOGF(LOG_DEBUG, "Removed device %s with handle %d, result %d\n", name.c_str(), kernelHandle, res);
} else } else
LOG(LOG_DEBUG, "Removing interface which was not opened successfully\n"); LOG(LOG_DEBUG, "Removing interface which was not opened successfully\n");
} }
NetworkInterface(const NetworkInterface&) = delete; NetworkInterface(const NetworkInterface&) = delete;
NetworkInterface& operator =(const NetworkInterface&) = delete; NetworkInterface& operator =(const NetworkInterface&) = delete;
bool openedSuccessfully() const { return kernelHandle >= 0; } bool openedSuccessfully() const { return kernelHandle >= 0; }
int getKernelHandle() const { return kernelHandle; } int getKernelHandle() const { return kernelHandle; }
int getIfIndex() const { return ifindex; }
const std::string& getName() const { return name; } const std::string& getName() const { return name; }
uint8_t* getRxBox() { return rxBox; } uint8_t* getRxBox() { return rxBox; }
const uint8_t* getRxBox() const { return rxBox; } const uint8_t* getRxBox() const { return rxBox; }
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::CANMessage>& msg) {
void reportErrorCount(const std::shared_ptr<icsneo::CANErrorCountMessage>& msg) {
LOGF(LOG_INFO, "%s CAN error count tx:%d rx:%d busoff:%d\n",
name.c_str(), msg->transmitErrorCount, msg->receiveErrorCount,
msg->busOff);
struct can_err_report err = {
.device = kernelHandle,
.state = (msg->busOff)?CAN_STATE_BUS_OFF:CAN_STATE_ERROR_ACTIVE,
.err_count = {
.txerr = msg->transmitErrorCount,
.rxerr = msg->receiveErrorCount,
},
};
if(ioctl(driver, SIOCSERRCOUNT, &err) < 0) {
LOGF(LOG_DEBUG, "error report ioctl failed %d\n", kernelHandle);
return;
}
}
template<typename T>
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::Frame>& msg) {
const auto neomessageGeneric = icsneo::CreateNeoMessage(msg); const auto neomessageGeneric = icsneo::CreateNeoMessage(msg);
if(neomessageGeneric.messageType != neomessagetype_t(icsneo::Message::Type::Frame)) { if(neomessageGeneric.messageType != neomessagetype_t(icsneo::Message::Type::Frame)) {
LOG(LOG_DEBUG, "could not create a neomessage_can_t\n"); LOG(LOG_DEBUG, "could not create a neomessage_can_t\n");
return; return;
} }
const auto& neomessage = *reinterpret_cast<const neomessage_can_t*>(&neomessageGeneric); if(msg->network.getType() == icsneo::Network::Type::CAN || msg->network.getType() == icsneo::Network::Type::Ethernet) {
const auto& neomessage = *reinterpret_cast<const T*>(&neomessageGeneric);
size_t bytesNeeded = sizeof(neomessage) + neomessage.length; size_t bytesNeeded = sizeof(neomessage) + neomessage.length;
std::lock_guard<std::mutex> lg(rxBoxLock); std::lock_guard<std::mutex> lg(rxBoxLock);
@@ -116,14 +288,211 @@ public:
rxBoxCurrentPosition = rxBox; rxBoxCurrentPosition = rxBox;
rxBoxMessageCount = 0; rxBoxMessageCount = 0;
} }
}
void update_bittiming(struct can_bittiming *bt)
{
struct can_clock clock = {
.freq = 80000000,
};
__u64 v64 = (__u64)bt->brp * 1000 * 1000 * 1000;
v64 = v64 / clock.freq;
bt->tq = (__u32)v64;
__u32 tseg = 1 + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
bt->bitrate = clock.freq / (bt->brp * tseg);
bt->sample_point = 1000 * (tseg - bt->phase_seg2) / tseg;
bt->sjw = std::max(1U, std::min(bt->phase_seg1, bt->phase_seg2 / 2));
}
void storeCanSettings(const CAN_SETTINGS *can, const CANFD_SETTINGS *canfd, bool termination) {
LOGF(LOG_INFO, "Baudrate:%d TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d ifdelay:%d\n",
can->Baudrate, can->TqSeg1, can->TqSeg2, can->TqProp, can->TqSync, can->BRP, can->innerFrameDelay25us);
LOGF(LOG_INFO, "FD Baudrate:%d TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d\n",
canfd->FDBaudrate, canfd->FDTqSeg1, canfd->FDTqSeg2, canfd->FDTqProp, canfd->FDTqSync, canfd->FDBRP);
LOGF(LOG_INFO, "FDMode:0x%x TransceiverMode:0x%x\n", canfd->FDMode, can->transceiver_mode);
bit_timing.prop_seg = can->TqProp;
bit_timing.phase_seg1 = can->TqSeg1;
bit_timing.phase_seg2 = can->TqSeg2;
bit_timing.brp = can->BRP + 1;
bit_timing.sjw = can->TqSync;
data_bit_timing.prop_seg = canfd->FDTqProp;
data_bit_timing.phase_seg1 = canfd->FDTqSeg1;
data_bit_timing.phase_seg2 = canfd->FDTqSeg2;
data_bit_timing.brp = canfd->FDBRP + 1;
data_bit_timing.sjw = canfd->FDTqSync;
this->termination = termination;
ctrl_mode = 0;
switch (canfd->FDMode) {
case NO_CANFD:
break;
ctrl_mode = 0;
case CANFD_ENABLED:
case CANFD_BRS_ENABLED:
ctrl_mode = CAN_CTRLMODE_FD_NON_ISO;
break;
case CANFD_ENABLED_ISO:
case CANFD_BRS_ENABLED_ISO:
ctrl_mode = CAN_CTRLMODE_FD;
break;
}
switch (can->transceiver_mode) {
case LOOPBACK:
ctrl_mode |= CAN_CTRLMODE_LOOPBACK;
break;
case LISTEN_ONLY:
case LISTEN_ALL:
ctrl_mode |= CAN_CTRLMODE_LISTENONLY;
break;
}
update_bittiming(&bit_timing);
if (canfd->FDMode != NO_CANFD) {
update_bittiming(&data_bit_timing);
} else {
data_bit_timing.bitrate = 0;
}
struct can_dev_settings settings = {
.device = kernelHandle,
.bittiming = bit_timing,
.data_bittiming = data_bit_timing,
.ctrl_mode = ctrl_mode,
.termination = termination,
};
if(ioctl(driver, SIOCSIFSETTINGS, &settings) < 0) {
LOGF(LOG_DEBUG, "device settings ioctl failed %d\n", kernelHandle);
return;
}
}
void setBittiming(struct can_bittiming *timing, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
if (timing->prop_seg == bit_timing.prop_seg
&& timing->phase_seg1 == bit_timing.phase_seg1
&& timing->phase_seg2 == bit_timing.phase_seg2
&& timing->sjw == bit_timing.sjw
&& timing->brp == bit_timing.brp) {
LOG(LOG_INFO, "no change in bittiming\n");
return;
}
CAN_SETTINGS *settings = device->settings->getMutableCANSettingsFor(netid);
settings->SetBaudrate = USE_TQ;
settings->TqSeg1 = timing->phase_seg1;
settings->TqSeg2 = timing->phase_seg2;
settings->TqSync = timing->sjw;
settings->TqProp = timing->prop_seg;
settings->BRP = timing->brp - 1;
LOGF(LOG_INFO, "Set Bittiming TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d\n",
settings->TqSeg1, settings->TqSeg2, settings->TqProp, settings->TqSync, settings->BRP);
if (! device->settings->apply() ) {
LOGF(LOG_ERR, "Unable to set bit timings for %s", name.c_str());
}
bit_timing = *timing;
}
void setDataBittiming(struct can_bittiming *timing, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
if (timing->prop_seg == data_bit_timing.prop_seg
&& timing->phase_seg1 == data_bit_timing.phase_seg1
&& timing->phase_seg2 == data_bit_timing.phase_seg2
&& timing->sjw == data_bit_timing.sjw
&& timing->brp == data_bit_timing.brp) {
return;
}
CANFD_SETTINGS *settings = device->settings->getMutableCANFDSettingsFor(netid);
settings->FDTqSeg1 = timing->phase_seg1;
settings->FDTqSeg2 = timing->phase_seg2;
settings->FDTqSync = timing->sjw;
settings->FDTqProp = timing->prop_seg;
settings->FDBRP = timing->brp - 1;
if (! device->settings->apply() ) {
LOGF(LOG_ERR, "Unable to set data bit timings for %s", name.c_str());
}
data_bit_timing = *timing;
}
void setCtrlMode(uint32_t mode, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
if (mode == ctrl_mode) {
return;
}
if ((mode & (CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_FD))
!= (ctrl_mode & (CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_FD))) {
CANFD_SETTINGS *settings = device->settings->getMutableCANFDSettingsFor(netid);
if (mode & CAN_CTRLMODE_FD_NON_ISO) {
if (bit_timing.bitrate == data_bit_timing.bitrate) {
settings->FDMode = CANFD_ENABLED;
} else {
settings->FDMode = CANFD_BRS_ENABLED;
}
} else if (mode & CAN_CTRLMODE_FD) {
if (bit_timing.bitrate == data_bit_timing.bitrate) {
settings->FDMode = CANFD_ENABLED_ISO;
} else {
settings->FDMode = CANFD_BRS_ENABLED_ISO;
}
} else {
settings->FDMode = NO_CANFD;
}
}
if ((mode & (CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LOOPBACK))
!= (ctrl_mode & (CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LOOPBACK))) {
CAN_SETTINGS *settings = device->settings->getMutableCANSettingsFor(netid);
if (mode & CAN_CTRLMODE_LISTENONLY) {
settings->transceiver_mode = LISTEN_ONLY;
} else if (mode & CAN_CTRLMODE_LOOPBACK) {
settings->transceiver_mode = LOOPBACK;
} else {
settings->transceiver_mode = NORMAL;
}
}
if (! device->settings->apply() ) {
LOGF(LOG_ERR, "Unable to set controller mode for %s", name.c_str());
}
ctrl_mode = mode;
}
void setTermination(bool termination, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
if (termination != this->termination) {
if (! device->settings->setTerminationFor(netid, termination) ||
! device->settings->apply() ) {
LOGF(LOG_ERR, "Unable to set termination for %s", name.c_str());
}
this->termination = termination;
}
}
private: private:
icsneo::Network::Type type;
std::string name; std::string name;
int kernelHandle = -1; int kernelHandle = -1;
int ifindex = -1;
std::mutex rxBoxLock; std::mutex rxBoxLock;
uint8_t* rxBox = nullptr; uint8_t* rxBox = nullptr;
uint8_t* rxBoxCurrentPosition = nullptr; uint8_t* rxBoxCurrentPosition = nullptr;
size_t rxBoxMessageCount = 0; size_t rxBoxMessageCount = 0;
struct can_bittiming bit_timing;
struct can_bittiming data_bit_timing;
uint32_t ctrl_mode;
bool termination;
}; };
class OpenDevice { class OpenDevice {
@@ -180,7 +549,7 @@ std::string sanitizeInterfaceName(std::string str) {
void header() { void header() {
std::cout << "The libicsneo SocketCAN Usermode Daemon\n"; std::cout << "The libicsneo SocketCAN Usermode Daemon\n";
std::cout << "Copyright Intrepid Control Systems, Inc. 2019\n\n"; std::cout << "Copyright Intrepid Control Systems, Inc. 2025\n\n";
std::cout << "Daemon v"; std::cout << "Daemon v";
std::cout << (int)ICSNEO_SOCKETCAN_BUILD_MAJOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_MINOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_PATCH; std::cout << (int)ICSNEO_SOCKETCAN_BUILD_MAJOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_MINOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_PATCH;
if(ICSNEO_SOCKETCAN_BUILD_METADATA[0] != '\0') if(ICSNEO_SOCKETCAN_BUILD_METADATA[0] != '\0')
@@ -197,13 +566,14 @@ void header() {
void usage(std::string executableName) { void usage(std::string executableName) {
std::cerr << "The libicsneo SocketCAN Usermode Daemon\n"; std::cerr << "The libicsneo SocketCAN Usermode Daemon\n";
std::cerr << "Copyright 2019-2020 Intrepid Control Systems, Inc.\n\n"; std::cerr << "Copyright 2019-2025 Intrepid Control Systems, Inc.\n\n";
std::cerr << "Usage: " << executableName << " [option]\n\n"; std::cerr << "Usage: " << executableName << " [option]\n\n";
std::cerr << "Options:\n"; std::cerr << "Options:\n";
std::cerr << "\t-d, --daemon\t\tRun as a daemon in the background\n"; std::cerr << "\t-d, --daemon\t\t\tRun as a daemon in the background\n";
std::cerr << "\t-h, -?, --help, --usage\t\tShow this help page\n"; std::cerr << "\t-h, -?, --help, --usage\t\t\tShow this help page\n";
std::cerr << "\t --devices\t\tList supported devices\n"; std::cerr << "\t --devices\t\t\tList supported devices\n";
std::cerr << "\t --filter <serial>\tOnly connect to devices with serial\n\t\t\t\t\tnumbers starting with this filter\n"; std::cerr << "\t --filter <serial>\t\tOnly connect to devices with serial\n\t\t\t\t\t\tnumbers starting with this filter\n";
std::cerr << "\t --scan-interval-ms <interval>\tDevice scan interval in ms\n\t\t\t\t\t\tIf 0, only a single scan is performed\n";
} }
void terminateSignal(int signal) { void terminateSignal(int signal) {
@@ -246,27 +616,28 @@ void searchForDevices() {
continue; continue;
} }
// Get the supported CAN networks // Get the supported networks
auto supportedNetworks = newDevice.device->getSupportedRXNetworks(); auto supportedNetworks = newDevice.device->getSupportedRXNetworks();
supportedNetworks.erase(std::remove_if(supportedNetworks.begin(), supportedNetworks.end(), [](const icsneo::Network& net) -> bool { supportedNetworks.erase(std::remove_if(supportedNetworks.begin(), supportedNetworks.end(), [](const icsneo::Network& net) -> bool {
return net.getType() != icsneo::Network::Type::CAN;// Only want CAN networks return net.getType() != icsneo::Network::Type::CAN && net.getType() != icsneo::Network::Type::Ethernet;
}), supportedNetworks.end()); }), supportedNetworks.end());
if(supportedNetworks.empty()) { if(supportedNetworks.empty()) {
if(firstTimeFailedToOpen) { if(firstTimeFailedToOpen) {
LOGF(LOG_INFO, "%s has no supported CAN networks\n", newDevice.device->describe().c_str()); LOGF(LOG_INFO, "%s has no supported networks\n", newDevice.device->describe().c_str());
failedToOpen.push_back(serial); failedToOpen.push_back(serial);
} }
continue; continue;
} }
// Create a network interface for each CAN network // Create a network interface for each network
for(const auto& net : supportedNetworks) { for(const auto& net : supportedNetworks) {
std::stringstream ss; std::stringstream ss;
ss << sanitizeInterfaceName(icsneo::Network::GetNetIDString(net.getNetID())) << "_" << serial; ss << sanitizeInterfaceName(icsneo::Network::GetNetIDString(net.getNetID())) << "_" << serial;
std::string interfaceName(ss.str()); std::string interfaceName(ss.str());
if(firstTimeFailedToOpen) if(firstTimeFailedToOpen)
LOGF(LOG_INFO, "Creating network interface %s\n", interfaceName.c_str()); LOGF(LOG_INFO, "Creating network interface %s\n", interfaceName.c_str());
newDevice.interfaces[net.getNetID()] = std::make_shared<NetworkInterface>(interfaceName);
newDevice.interfaces[net.getNetID()] = std::make_shared<NetworkInterface>(interfaceName, net.getType(), newDevice.device->getType());
LOGF(LOG_INFO, "Created network interface %s\n", interfaceName.c_str()); LOGF(LOG_INFO, "Created network interface %s\n", interfaceName.c_str());
} }
bool failedToCreateNetworkInterfaces = false; bool failedToCreateNetworkInterfaces = false;
@@ -281,12 +652,25 @@ void searchForDevices() {
LOGF(LOG_INFO, "%s failed to create network interfaces. Will keep trying...\n", newDevice.device->describe().c_str()); LOGF(LOG_INFO, "%s failed to create network interfaces. Will keep trying...\n", newDevice.device->describe().c_str());
failedToOpen.push_back(serial); failedToOpen.push_back(serial);
} }
continue; continue;
} }
if (driverMinor > 0) {
for(const auto& net : supportedNetworks) {
if (net.getType() != icsneo::Network::Type::CAN)
continue;
const CAN_SETTINGS *can = newDevice.device->settings->getCANSettingsFor(net.getNetID());
const CANFD_SETTINGS *fd = newDevice.device->settings->getCANFDSettingsFor(net.getNetID());
bool termination = newDevice.device->settings->isTerminationEnabledFor(net.getNetID())
.value_or(false);
newDevice.interfaces[net.getNetID()]->storeCanSettings(can, fd, termination);
}
}
// Create rx listener // Create rx listener
newDevice.device->addMessageCallback(icsneo::CANMessageCallback([serial](std::shared_ptr<icsneo::Message> message) { newDevice.device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([serial](std::shared_ptr<icsneo::Message> message) {
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message); const auto frame = std::static_pointer_cast<icsneo::Frame>(message);
const auto messageType = frame->network.getType();
const OpenDevice* openDevice = nullptr; const OpenDevice* openDevice = nullptr;
std::lock_guard<std::mutex> lg(openDevicesMutex); std::lock_guard<std::mutex> lg(openDevicesMutex);
for(const auto& dev : openDevices) { for(const auto& dev : openDevices) {
@@ -295,13 +679,22 @@ void searchForDevices() {
break; break;
} }
} }
if(!openDevice) { if(frame->type == icsneo::Message::Type::CANErrorCount) {
LOG(LOG_ERR, "Dropping message, no open device\n"); const auto errmsg = std::static_pointer_cast<icsneo::CANErrorCountMessage>(message);
openDevice->interfaces.at(frame->network.getNetID())->reportErrorCount(errmsg);
return;
}
if(frame->type != icsneo::Message::Type::Frame) {
LOG(LOG_ERR, "Dropping message: received invalid message type, expected RawMessage\n");
return; return;
} }
// todo might throw if(messageType == icsneo::Network::Type::CAN) {
openDevice->interfaces.at(canMessage->network.getNetID())->addReceivedMessageToQueue(canMessage); openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_can_t>(frame);
} else if(messageType == icsneo::Network::Type::Ethernet) {
openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_eth_t>(frame);
} else
LOG(LOG_ERR, "Dropping message, only CAN and Ethernet are currently supported\n");
})); }));
LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str()); LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str());
@@ -353,7 +746,10 @@ void searchForDevices() {
void deviceSearchThread() { void deviceSearchThread() {
while(!stopRunning) { while(!stopRunning) {
searchForDevices(); searchForDevices();
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); if(scanIntervalMs == 0) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(scanIntervalMs));
} }
} }
@@ -374,6 +770,21 @@ int main(int argc, char** argv) {
} else if(arg == "--filter" && i + 1 <= argc) { } else if(arg == "--filter" && i + 1 <= argc) {
serialFilter = argv[++i]; serialFilter = argv[++i];
transform(serialFilter.begin(), serialFilter.end(), serialFilter.begin(), ::toupper); transform(serialFilter.begin(), serialFilter.end(), serialFilter.begin(), ::toupper);
} else if(arg == "--scan-interval-ms" && i + 1 <= argc) {
try {
scanIntervalMs = std::stoi(argv[++i]);
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid input for scan-interval-ms\n";
return EX_USAGE;
} catch (const std::out_of_range& e) {
std::cerr << "Out of range input for scan-interval-ms\n";
return EX_USAGE;
}
if(scanIntervalMs < 0) {
std::cerr << "Invalid input for scan-interval-ms\n";
return EX_USAGE;
}
} else { } else {
usage(argv[0]); usage(argv[0]);
return EX_USAGE; return EX_USAGE;
@@ -417,7 +828,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "Driver v" << driverMajor << '.' << driverMinor << '.' << driverPatch << "\n\n"; std::cout << "Driver v" << driverMajor << '.' << driverMinor << '.' << driverPatch << "\n\n";
if(driverMajor > 2) { if(driverMajor > 3) {
LOG(LOG_ERR, "This version of the usermode daemon is too old to work with this driver\nPlease ensure that both the usermode daemon and kernel driver are up to date\n"); LOG(LOG_ERR, "This version of the usermode daemon is too old to work with this driver\nPlease ensure that both the usermode daemon and kernel driver are up to date\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -443,6 +854,12 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int netlink_socket = open_netlink_socket();
if (netlink_socket < 0) {
LOGF(LOG_ERR, "Unable to open netlink socket\nError %d: %s\n", errno, strerror(errno));
return EXIT_FAILURE;
}
// Daemonize if necessary // Daemonize if necessary
if(runningAsDaemon) { if(runningAsDaemon) {
LOG(LOG_INFO, "The daemon will now continue to run in the background\n"); LOG(LOG_INFO, "The daemon will now continue to run in the background\n");
@@ -462,17 +879,49 @@ int main(int argc, char** argv) {
fd_set fds; fd_set fds;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(driver, &fds); FD_SET(driver, &fds);
FD_SET(netlink_socket, &fds);
int max_fd = (driver > netlink_socket)?driver:netlink_socket;
struct timeval timeout = {}; struct timeval timeout = {};
timeout.tv_sec = 1; timeout.tv_sec = 1;
auto ret = select(driver + 1, &fds, NULL, NULL, &timeout); auto ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
if(ret == -1) { if(ret == -1) {
// Fatal error // Fatal error
LOGF(LOG_ERR, "Error waiting for tx messages: %s\n", strerror(errno)); LOGF(LOG_ERR, "Error waiting for tx messages: %s\n", strerror(errno));
stopRunning = true; stopRunning = true;
break; break;
} else if(ret != 0) { }
if (FD_ISSET(netlink_socket, &fds)) {
// Kernel sent some information via netlink, handle it.
read_netlink_msgs(netlink_socket, [](int ifindex, int type, void *data) {
for(auto& dev : openDevices) {
for(auto& netifPair : dev.interfaces) {
auto netid = netifPair.first;
auto iface = netifPair.second;
if (iface->getIfIndex() != ifindex) {
continue;
}
switch (type) {
case IFLA_CAN_BITTIMING:
iface->setBittiming((struct can_bittiming *) data, dev.device, netid);
break;
case IFLA_CAN_DATA_BITTIMING:
iface->setDataBittiming((struct can_bittiming *) data, dev.device, netid);
break;
case IFLA_CAN_TERMINATION:
iface->setTermination(*((uint16_t *) data) != 0, dev.device, netid);
break;
case IFLA_CAN_CTRLMODE:
iface->setCtrlMode(((struct can_ctrlmode *) data)->flags, dev.device, netid);
break;
}
}
}
});
}
if (FD_ISSET(driver, &fds)) {
// Kernel says there are some new transmit messages waiting to go out. // Kernel says there are some new transmit messages waiting to go out.
// Call read() to find out which box they're in and how many // Call read() to find out which box they're in and how many
struct intrepid_pending_tx_info info; struct intrepid_pending_tx_info info;
@@ -485,6 +934,36 @@ int main(int argc, char** argv) {
LOGF(LOG_ERR, "Unexpected number of bytes read, expected %d got %d\n", (int)sizeof(info), (int)r); LOGF(LOG_ERR, "Unexpected number of bytes read, expected %d got %d\n", (int)sizeof(info), (int)r);
stopRunning = true; stopRunning = true;
break; break;
} else if (info.tx_box_index < 0) {
// Baudrate changed in kernel
int dev_idx = -(info.tx_box_index + 1);
LOGF(LOG_INFO, "Baudrate change, device %d, baudrate %d fd_baudrate %ld\n",
dev_idx, info.count, info.bytes);
/* fd baudrate is zero if fd mode is disabled in kernel
* set fd baudrate equal to baudrate */
if (info.bytes == 0) {
info.bytes = info.count;
}
for(auto& dev : openDevices) {
for(auto& netifPair : dev.interfaces) {
auto netid = netifPair.first;
if(netifPair.second->getKernelHandle() != dev_idx)
continue;
if (! dev.device->settings->setBaudrateFor(netid, info.count) ) {
LOGF(LOG_ERR, "Unable to set baudrate for device %s\n",
netifPair.second->getName().c_str());
} else if (! dev.device->settings->setFDBaudrateFor(netid, info.bytes)) {
LOGF(LOG_ERR, "Unable to set fd baudrate for device %s\n",
netifPair.second->getName().c_str());
} else if (! dev.device->settings->setTerminationFor(netid, false)) {
LOGF(LOG_ERR, "Unable to set termination for device %s\n",
netifPair.second->getName().c_str());
} else if (! dev.device->settings->apply()) {
LOGF(LOG_ERR, "Unable to apply settings for device %s\n",
netifPair.second->getName().c_str());
}
}
}
} else { } else {
// Send! // Send!
uint8_t* currentPosition = GET_TX_BOX(info.tx_box_index); uint8_t* currentPosition = GET_TX_BOX(info.tx_box_index);
@@ -494,8 +973,8 @@ int main(int argc, char** argv) {
msg->data = currentPosition; msg->data = currentPosition;
currentPosition += msg->length; currentPosition += msg->length;
if(msg->type != neonettype_t(icsneo::Network::Type::CAN)) { if(msg->type != neonettype_t(icsneo::Network::Type::CAN) && msg->type != neonettype_t(icsneo::Network::Type::Ethernet)) {
LOG(LOG_ERR, "Message dropped, kernel sent a non-CAN message\n"); LOG(LOG_ERR, "Message dropped, kernel sent a non-CAN/Ethernet message\n");
continue; continue;
} }
+99
View File
@@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <linux/if_link.h>
#include <linux/if_arp.h>
#include <linux/netlink.h>
#include <linux/can/netlink.h>
#include <linux/rtnetlink.h>
#include "netlink.h"
bool read_netlink_msgs(int s, msg_callback callback)
{
struct nlmsghdr buf[8192/sizeof(struct nlmsghdr)];
struct nlmsghdr *nh;
int len = recv(s, buf, sizeof(buf), 0);
for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
nh = NLMSG_NEXT (nh, len)) {
/* The end of multipart message */
if (nh->nlmsg_type == NLMSG_DONE) {
printf("Done\n");
return true;
}
if (nh->nlmsg_type == NLMSG_ERROR) {
printf("Error\n");
} else if (nh->nlmsg_type == RTM_NEWLINK) {
struct ifinfomsg *iface = NLMSG_DATA(nh);
int attrlen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
if (iface->ifi_type == ARPHRD_CAN) {
for (struct rtattr *rta = IFLA_RTA(iface); RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
switch(rta->rta_type) {
case IFLA_LINKINFO:
{
int attr2len = RTA_PAYLOAD(rta);
char *kind = NULL;
void *data = NULL;
int data_len;
for (struct rtattr *rta2 = RTA_DATA(rta); RTA_OK(rta2, attr2len);
rta2 = RTA_NEXT(rta2, attr2len)) {
switch (rta2->rta_type) {
case IFLA_INFO_KIND:
kind = RTA_DATA(rta2);
break;
case IFLA_INFO_DATA:
data = RTA_DATA(rta2);
data_len = RTA_PAYLOAD(rta2);
break;
}
}
if (kind && strcmp(kind, "can") == 0 && data) {
attr2len = data_len;
for (struct rtattr *rta2 = data; RTA_OK(rta2, attr2len);
rta2 = RTA_NEXT(rta2, attr2len)) {
if (callback) {
callback(iface->ifi_index, rta2->rta_type, RTA_DATA(rta2));
}
}
}
}
break;
}
}
}
}
}
}
int open_netlink_socket()
{
int s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
if (s < 0) {
return -1;
}
struct sockaddr_nl addr = {
.nl_family = AF_NETLINK,
.nl_groups = RTMGRP_LINK,
};
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return -1;
}
int group = RTMGRP_LINK;
if (setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group))) {
return -1;
}
return s;
}
+13
View File
@@ -0,0 +1,13 @@
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*msg_callback)(int /* ifindex */, int /* rta_type */, void * /* data */);
bool read_netlink_msgs(int s, msg_callback callback);
int open_netlink_socket();
#ifdef __cplusplus
}
#endif