• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

traintastic / traintastic / 23681802330

28 Mar 2026 08:58AM UTC coverage: 26.146% (-0.004%) from 26.15%
23681802330

push

github

reinder
[cbus] socketcan: use kernel filtering, only accept standard non rtr frames

0 of 3 new or added lines in 1 file covered. (0.0%)

120 existing lines in 3 files now uncovered.

8256 of 31576 relevant lines covered (26.15%)

182.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/server/src/hardware/protocol/can/iohandler/socketcaniohandler.cpp
1
/**
2
 * This file is part of Traintastic,
3
 * see <https://github.com/traintastic/traintastic>.
4
 *
5
 * Copyright (C) 2023-2026 Reinder Feenstra
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License
9
 * as published by the Free Software Foundation; either version 2
10
 * of the License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
 */
21

22
#include "socketcaniohandler.hpp"
23
#include <cstring>
24
#include <sys/types.h>
25
#include <sys/socket.h>
26
#include <sys/ioctl.h>
27
#include <net/if.h>
28
#include <linux/can/raw.h>
29
#include "../../../../core/eventloop.hpp"
30
#include "../../../../log/log.hpp"
31
#include "../../../../log/logmessageexception.hpp"
32

33
namespace CAN {
34

35
SocketCANIOHandler::SocketCANIOHandler(boost::asio::io_context& ioContext, const std::string& interface, std::string logId, OnReceive onReceive, OnError onError, std::span<Filter> filter)
×
36
  : m_stream{ioContext}
×
37
  , m_logId{std::move(logId)}
×
38
  , m_onReceive{std::move(onReceive)}
×
39
  , m_onError{std::move(onError)}
×
40
{
41
  assert(m_onReceive);
×
42
  assert(m_onError);
×
43

44
  int fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
×
45
  if(fd < 0)
×
46
  {
47
    throw LogMessageException(LogMessage::E2022_SOCKET_CREATE_FAILED_X, std::string_view(strerror(errno)));
×
48
  }
49

50
  struct ifreq ifr;
51
  std::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ - 1);
×
52
  ifr.ifr_name[IFNAMSIZ - 1] = '\0';
×
53
  if(ioctl(fd, SIOCGIFINDEX, &ifr) < 0)
×
54
  {
55
    close(fd);
×
56
    throw LogMessageException(LogMessage::E2023_SOCKET_IOCTL_FAILED_X, std::string_view(strerror(errno)));
×
57
  }
58

59
  struct sockaddr_can addr;
60
  addr.can_family = AF_CAN;
×
61
  addr.can_ifindex = ifr.ifr_ifindex;
×
62
  if(bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
×
63
  {
64
    close(fd);
×
65
    throw LogMessageException(LogMessage::E2006_SOCKET_BIND_FAILED_X, std::string_view(strerror(errno)));
×
66
  }
67

68
  if(!filter.empty() && setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, filter.data(), sizeof(Filter) * filter.size()) < 0)
×
69
  {
UNCOV
70
    close(fd);
×
71
    throw LogMessageException(LogMessage::E2028_SOCKET_SETSOCKOPT_FAILED_X, std::string_view(strerror(errno)));
×
72
  }
73

74
  m_stream.assign(fd);
×
UNCOV
75
}
×
76

UNCOV
77
void SocketCANIOHandler::start()
×
78
{
79
  read();
×
80
}
×
81

82
void SocketCANIOHandler::stop()
×
83
{
84
  m_stream.cancel();
×
UNCOV
85
  m_stream.close();
×
86
}
×
87

UNCOV
88
bool SocketCANIOHandler::send(const Frame& frame)
×
89
{
UNCOV
90
  if(m_writeBufferOffset + 1 > m_writeBuffer.size())
×
91
  {
UNCOV
92
    return false;
×
93
  }
94

95
  const bool wasEmpty = m_writeBufferOffset == 0;
×
96

97
  std::memcpy(&m_writeBuffer[m_writeBufferOffset], &frame, sizeof(frame));
×
98

UNCOV
99
  m_writeBufferOffset++;
×
100

UNCOV
101
  if(wasEmpty)
×
102
  {
103
    write();
×
104
  }
105

106
  return true;
×
107
}
108

UNCOV
109
void SocketCANIOHandler::read()
×
110
{
111
  m_stream.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset * frameSize, (m_readBuffer.size() - m_readBufferOffset) * frameSize),
×
112
    [this](const boost::system::error_code& ec, std::size_t bytesTransferred)
×
113
    {
114
      if(!ec)
×
115
      {
116
        auto framesTransferred = bytesTransferred / frameSize;
×
117
        const auto* frame = reinterpret_cast<const Frame*>(m_readBuffer.data());
×
118
        framesTransferred += m_readBufferOffset;
×
119

UNCOV
120
        while(framesTransferred > 0)
×
121
        {
UNCOV
122
          m_onReceive(*frame);
×
123
          frame++;
×
UNCOV
124
          framesTransferred--;
×
125
        }
126

127
        if(framesTransferred != 0) [[unlikely]]
×
128
        {
129
          memmove(m_readBuffer.data(), frame, framesTransferred * frameSize);
×
130
        }
131
        m_readBufferOffset = framesTransferred;
×
132

UNCOV
133
        read();
×
134
      }
135
      else if(ec != boost::asio::error::operation_aborted)
×
136
      {
UNCOV
137
        EventLoop::call(
×
138
          [this, ec]()
×
139
          {
UNCOV
140
            Log::log(m_logId, LogMessage::E2008_SOCKET_READ_FAILED_X, ec);
×
141
            m_onError();
×
UNCOV
142
          });
×
143
      }
144
    });
×
UNCOV
145
}
×
146

UNCOV
147
void SocketCANIOHandler::write()
×
148
{
149
  m_stream.async_write_some(boost::asio::buffer(m_writeBuffer.data(), m_writeBufferOffset * frameSize),
×
UNCOV
150
    [this](const boost::system::error_code& ec, std::size_t bytesTransferred)
×
151
    {
152
      if(!ec)
×
153
      {
UNCOV
154
        const auto framesTransferred = bytesTransferred / frameSize;
×
UNCOV
155
        if(framesTransferred < m_writeBufferOffset)
×
156
        {
UNCOV
157
          m_writeBufferOffset -= framesTransferred;
×
158
          memmove(m_writeBuffer.data(), m_writeBuffer.data() + framesTransferred * frameSize, m_writeBufferOffset);
×
UNCOV
159
          write();
×
160
        }
161
        else
UNCOV
162
          m_writeBufferOffset = 0;
×
163
      }
164
      else if(ec != boost::asio::error::operation_aborted)
×
165
      {
UNCOV
166
        EventLoop::call(
×
167
          [this, ec]()
×
168
          {
UNCOV
169
            Log::log(m_logId, LogMessage::E2007_SOCKET_WRITE_FAILED_X, ec);
×
UNCOV
170
            m_onError();
×
UNCOV
171
          });
×
172
      }
UNCOV
173
    });
×
UNCOV
174
}
×
175

176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc