/
Sangak
/
ardupilot
Обзор
Документация
Войти
/
Sangak
/
ardupilot
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
libraries/AP_CANManager/AP_MAVLinkCAN.cpp
360 строк
12 KB
Peter Barker
AP_MAVLinkCAN: reject negative CAN bus index in filter modify
17 июн 2026, 10:21
17 июн 2026, 10:21
562d10e
Код
Авторство
О чём код?
/* * This file is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "AP_MAVLinkCAN.h" #include <AP_HAL/utility/sparse-endian.h> #include <AP_Common/sorting.h> #if AP_MAVLINKCAN_ENABLED extern const AP_HAL::HAL& hal; static AP_MAVLinkCAN *singleton; AP_MAVLinkCAN *AP_MAVLinkCAN::ensure_singleton() { if (singleton == nullptr) { singleton = NEW_NOTHROW AP_MAVLinkCAN(); } return singleton; } bool AP_MAVLinkCAN::handle_can_forward(mavlink_channel_t chan, const mavlink_command_int_t &packet, const mavlink_message_t &msg) { auto *s = ensure_singleton(); if (s == nullptr) { return false; } return singleton->_handle_can_forward(chan, packet, msg); } void AP_MAVLinkCAN::handle_can_frame(const mavlink_message_t &msg) { auto *s = ensure_singleton(); if (s == nullptr) { return; } singleton->_handle_can_frame(msg); } void AP_MAVLinkCAN::handle_can_filter_modify(const mavlink_message_t &msg) { auto *s = ensure_singleton(); if (s == nullptr) { return; } singleton->_handle_can_filter_modify(msg); } /* handle MAV_CMD_CAN_FORWARD mavlink long command */ bool AP_MAVLinkCAN::_handle_can_forward(mavlink_channel_t chan, const mavlink_command_int_t &packet, const mavlink_message_t &msg) { WITH_SEMAPHORE(can_forward.sem); const int8_t bus = int8_t(packet.param1)-1; if (bus == -1) { /* a request to stop forwarding */ if (can_forward.callback_id != 0) { hal.can[can_forward.callback_bus]->unregister_frame_callback(can_forward.callback_id); can_forward.callback_id = 0; } return true; } if (bus < 0 || bus >= HAL_NUM_CAN_IFACES || hal.can[bus] == nullptr) { return false; } if (can_forward.callback_id != 0 && can_forward.callback_bus != bus && can_forward.callback_bus < HAL_NUM_CAN_IFACES) { /* the client is changing which bus they are monitoring, unregister from the previous bus */ hal.can[can_forward.callback_bus]->unregister_frame_callback(can_forward.callback_id); can_forward.callback_id = 0; } if (can_forward.callback_id == 0 && !hal.can[bus]->register_frame_callback( FUNCTOR_BIND_MEMBER(&AP_MAVLinkCAN::can_frame_callback, void, uint8_t, const AP_HAL::CANFrame &, AP_HAL::CANIface::CanIOFlags), can_forward.callback_id)) { // failed to register the callback return false; } can_forward.callback_bus = bus; can_forward.last_callback_enable_ms = AP_HAL::millis(); can_forward.chan = chan; can_forward.system_id = msg.sysid; can_forward.component_id = msg.compid; return true; } /* handle a CAN_FRAME packet */ void AP_MAVLinkCAN::_handle_can_frame(const mavlink_message_t &msg) { if (frame_buffer == nullptr) { // allocate frame buffer // 20 is good for firmware upload uint8_t buffer_size = 20; WITH_SEMAPHORE(frame_buffer_sem); while (frame_buffer == nullptr && buffer_size > 0) { // we'd like 20 frames, but will live with less frame_buffer = NEW_NOTHROW ObjectBuffer<BufferFrame>(buffer_size); if (frame_buffer != nullptr && frame_buffer->get_size() != 0) { // register a callback for when frames can't be sent immediately hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_MAVLinkCAN::process_frame_buffer, void)); break; } delete frame_buffer; frame_buffer = nullptr; buffer_size /= 2; } if (frame_buffer == nullptr) { // discard the frames return; } } switch (msg.msgid) { case MAVLINK_MSG_ID_CAN_FRAME: { mavlink_can_frame_t p; mavlink_msg_can_frame_decode(&msg, &p); if (p.bus >= HAL_NUM_CAN_IFACES || hal.can[p.bus] == nullptr) { return; } struct BufferFrame frame { bus : p.bus, frame : AP_HAL::CANFrame(p.id, p.data, p.len) }; { WITH_SEMAPHORE(frame_buffer_sem); frame_buffer->push(frame); } break; } #if HAL_CANFD_SUPPORTED case MAVLINK_MSG_ID_CANFD_FRAME: { mavlink_canfd_frame_t p; mavlink_msg_canfd_frame_decode(&msg, &p); if (p.bus >= HAL_NUM_CAN_IFACES || hal.can[p.bus] == nullptr) { return; } struct BufferFrame frame { bus : p.bus, frame : AP_HAL::CANFrame(p.id, p.data, p.len, true) }; { WITH_SEMAPHORE(frame_buffer_sem); frame_buffer->push(frame); } break; } #endif } process_frame_buffer(); } /* process the frame buffer */ void AP_MAVLinkCAN::process_frame_buffer() { while (frame_buffer) { WITH_SEMAPHORE(frame_buffer_sem); struct BufferFrame frame; const uint16_t timeout_us = 2000; if (!frame_buffer->peek(frame)) { // no frames in the queue break; } const int16_t retcode = hal.can[frame.bus]->send(frame.frame, AP_HAL::micros64() + timeout_us, AP_HAL::CANIface::IsForwardedFrame); if (retcode == 0) { // no space in the CAN output slots, try again later break; } // retcode == 1 means sent, -1 means a frame that can't be // sent. Either way we should remove from the queue frame_buffer->pop(); } } /* handle a CAN_FILTER_MODIFY packet */ void AP_MAVLinkCAN::_handle_can_filter_modify(const mavlink_message_t &msg) { mavlink_can_filter_modify_t p; mavlink_msg_can_filter_modify_decode(&msg, &p); const int8_t bus = int8_t(p.bus)-1; if (bus < 0 || bus >= HAL_NUM_CAN_IFACES || hal.can[bus] == nullptr) { return; } if (p.num_ids > ARRAY_SIZE(p.ids)) { return; } uint16_t *new_ids = nullptr; uint16_t num_new_ids = 0; WITH_SEMAPHORE(can_forward.sem); // sort the list, so we can bisection search and the array // operations below are efficient insertion_sort_uint16(p.ids, p.num_ids); switch (p.operation) { case CAN_FILTER_REPLACE: { if (p.num_ids == 0) { can_forward.num_filter_ids = 0; delete[] can_forward.filter_ids; can_forward.filter_ids = nullptr; return; } if (p.num_ids == can_forward.num_filter_ids && memcmp(p.ids, can_forward.filter_ids, p.num_ids*sizeof(uint16_t)) == 0) { // common case of replacing with identical list return; } new_ids = NEW_NOTHROW uint16_t[p.num_ids]; if (new_ids != nullptr) { num_new_ids = p.num_ids; memcpy((void*)new_ids, (const void *)p.ids, p.num_ids*sizeof(uint16_t)); } break; } case CAN_FILTER_ADD: { if (common_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids, p.ids, p.num_ids) == p.num_ids) { // nothing changing return; } new_ids = NEW_NOTHROW uint16_t[can_forward.num_filter_ids+p.num_ids]; if (new_ids == nullptr) { return; } if (can_forward.num_filter_ids != 0) { memcpy(new_ids, can_forward.filter_ids, can_forward.num_filter_ids*sizeof(uint16_t)); } memcpy(&new_ids[can_forward.num_filter_ids], p.ids, p.num_ids*sizeof(uint16_t)); insertion_sort_uint16(new_ids, can_forward.num_filter_ids+p.num_ids); num_new_ids = remove_duplicates_uint16(new_ids, can_forward.num_filter_ids+p.num_ids); break; } case CAN_FILTER_REMOVE: { if (common_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids, p.ids, p.num_ids) == 0) { // nothing changing return; } can_forward.num_filter_ids = remove_list_uint16(can_forward.filter_ids, can_forward.num_filter_ids, p.ids, p.num_ids); if (can_forward.num_filter_ids == 0) { delete[] can_forward.filter_ids; can_forward.filter_ids = nullptr; } break; } } if (new_ids != nullptr) { // handle common case of no change if (num_new_ids == can_forward.num_filter_ids && memcmp(new_ids, can_forward.filter_ids, num_new_ids*sizeof(uint16_t)) == 0) { delete[] new_ids; } else { // put the new list in place delete[] can_forward.filter_ids; can_forward.filter_ids = new_ids; can_forward.num_filter_ids = num_new_ids; } } } /* handler for CAN frames from the registered callback, sending frames out as CAN_FRAME or CANFD_FRAME messages */ void AP_MAVLinkCAN::can_frame_callback(uint8_t bus, const AP_HAL::CANFrame &frame, AP_HAL::CANIface::CanIOFlags flags) { mavlink_channel_t chan; uint8_t system_id; uint8_t component_id; { WITH_SEMAPHORE(can_forward.sem); if (bus != can_forward.callback_bus) { // we are not registered for forwarding this bus, discard frame return; } if (can_forward.frame_counter++ == 100) { // check every 100 frames for disabling CAN_FRAME send // we stop sending after 5s if the client stops // sending MAV_CMD_CAN_FORWARD requests if (can_forward.callback_id != 0 && AP_HAL::millis() - can_forward.last_callback_enable_ms > 5000) { hal.can[bus]->unregister_frame_callback(can_forward.callback_id); can_forward.callback_id = 0; return; } can_forward.frame_counter = 0; } if (can_forward.filter_ids != nullptr) { // work out ID of this frame uint16_t id = 0; if ((frame.id&0xff) != 0) { // not anonymous if (frame.id & 0x80) { // service message id = uint8_t(frame.id>>16); } else { // message frame id = uint16_t(frame.id>>8); } } if (!bisect_search_uint16(can_forward.filter_ids, can_forward.num_filter_ids, id)) { return; } } // remeber destination while we hold the mutex chan = can_forward.chan; system_id = can_forward.system_id; component_id = can_forward.component_id; } // the rest is run without the can_forward.sem WITH_SEMAPHORE(comm_chan_lock(chan)); const uint8_t data_len = AP_HAL::CANFrame::dlcToDataLength(frame.dlc); #if HAL_CANFD_SUPPORTED if (frame.isCanFDFrame()) { if (HAVE_PAYLOAD_SPACE(chan, CANFD_FRAME)) { mavlink_msg_canfd_frame_send(chan, system_id, component_id, bus, data_len, frame.id, const_cast<uint8_t*>(frame.data)); } } else #endif { if (HAVE_PAYLOAD_SPACE(chan, CAN_FRAME)) { mavlink_msg_can_frame_send(chan, system_id, component_id, bus, data_len, frame.id, const_cast<uint8_t*>(frame.data)); } } } #endif // AP_MAVLINKCAN_ENABLED