SocketCAN 命名空间 VCAN VXCAN CANGW 举例

NAMESPACE

namespaces, 命名空间, 将全局系统资源包装在抽象中, 使命名空间中的进程看起来拥有自己全局资源的独立实例. 命名空间的一个用途是实现容器.

Linux 命名空间类型及隔离物(Isolates):

  • Cgroup, Cgroup根目录
  • IPC, System V IPC, POSIX消息队列
  • Network, 网络设备, 堆叠, 端口等
  • Mount, 挂载点
  • PID, 进程ID
  • Time, 启动和单调时钟
  • User, 用户和组ID
  • UTS, 主机名和NIS域名

随着不断发展, 命名空间的类型也可能继续增删. SocketCAN 属于网络, 所以本篇主要是用到 Network 网络命名空间.

SocketCAN

SocketCAN, 是Linux的CAN协议实现, 使用 Berkeley socket API, Linux网络堆栈, 把 CAN 设备驱动实现为网络接口. CAN 的 socket API 设计与 TCP/IP 协议尽可能相似, 熟悉网络编程就能轻松的使用 SocketCAN.

SocketCAN 网络层协议和帧处理 的参考示意图:

在这里插入图片描述

其中:

  • CAN_RAW, 读取和写入 CAN 帧, 经过了接收过滤器(receive filters), Linux ns时间戳, 单个CAN口允许多个应用独立运行, 本地回显可实现网络透传, CAN_RAW 经常配合 bind 使用
  • CAN_BCW, Broadcast Manager, 循环消息的计时器和过滤器支持, 收发路径功能, 逐位过滤 CAN 帧 payload, 检测超时, 多路 CAN 消息的即时数据更新, CAN_BCW 需要配合 connect 使用, 当使用 recvfrom() 而不是 read() 检索BCM套接字消息时, can_ifindex 提供了原始 CAN 接口. 通过 CAN_BCM 可以方便的把 CAN 对接到其它接口如 ETH, UART 等
  • ISO-TP, ISO 15765-2 定义的 CAN 传输协议, 已经合并进 Linux 主线内核 5.10 及以后的版本. ISO-TP 将较长消息分割成多帧, 添加元数据(metadata), 允许接收方解释各个帧并重新组合成完整消息包. 先前版本 payload 限制最大4095字节, ISO 15765-2:2016 之后的版本扩大为 2^32-1 = 4294967295 字节(4GB -1)
  • CAN_GW, 基于Linux内核的 CAN 帧路由, 可使用 PF_CAN 接收滤波器, NET_RX 软中断, 基于 PF_NETLINK 的类似 iptables 的配置接口, 可动态修改CAN帧, 用 AND/OR/XOR/SET 操作改变 CAN 标识符, DLC, payload 数据等, 修改后可计算 XOR 和 CRC8, 支持不同的 CRC8 配置(1U8, 16U8, SFFID_XOR)

这里顺便列下 ISO 15765 (headlined Road vehicles — Diagnostic communication over Controller Area Network (DoCAN)):

  • ISO 15765-1, 通用信息和用例定义
  • ISO 15765-2, 传输协议和网络层服务 (ISO-TP)
  • ISO 15765-3, 实现统一诊断服务 (UDS on CAN), 已过时, 替换为 ISO14229-3 (CAN, ETH, UART => DoCAN, DoIP等)
  • ISO 15765-4, emissions 等相关系统的需求

以 Orin 为例, 一个典型的2路 SocketCAN 的启动脚本:

#!/bin/sh

# 寄存器配置
sudo busybox devmem 0x0c303000 32 0x0000C400
sudo busybox devmem 0x0c303008 32 0x0000C458
sudo busybox devmem 0x0c303010 32 0x0000C400
sudo busybox devmem 0x0c303018 32 0x0000C458

# Module 加载
sudo modprobe can
sudo modprobe can_raw
sudo modprobe mttcan

# TDC 配置, 以支持 2M 以上 CANFD
sudo sh -c 'echo 0x600 > /sys/devices/platform/c310000.mttcan/net/can0/tdc_offset'
sudo sh -c 'echo 0x600 > /sys/devices/platform/c320000.mttcan/net/can1/tdc_offset'

# CAN0 配置
sudo ip link set down can0
sudo ip link set can0 type can bitrate 500000 sample-point 0.8 dbitrate 5000000 dsample-point 0.8 berr-reporting on fd on restart-ms 100
sudo ip link set up can0 mtu 72
sudo ifconfig can0 txqueuelen 1000
# ip -s -d link show can0

# CAN1 配置
sudo ip link set down can1
sudo ip link set can1 type can bitrate 500000 sample-point 0.8 dbitrate 5000000 dsample-point 0.8 berr-reporting on fd on restart-ms 100
sudo ip link set up can1 mtu 72
sudo ifconfig can1 txqueuelen 1000
# ip -s -d link show can1

一个典型的 SocketCAN 的应用初始化例子如下:

{
	int s;
    struct sockaddr_can addr;
    struct ifreq ifr;

    if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        perror("Error while opening socket");
        return -1;
    }

    strcpy(ifr.ifr_name, "can0" );
    ioctl(s, SIOCGIFINDEX, &ifr);

    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    
    // CANFD 支持
    int enable_canfd = 1;
    if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
		&enable_canfd, sizeof(enable_canfd))) {
		printf("error when enabling CAN FD support\n");
		return 1;
	}
	
	// 滤波器, 略

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("Error in socket bind");
        return -2;
    }
}

初始化后可以使用正常的 read 和 write, 多个 socketcan 接口可以类似网络那样使用 epoll 等进行管理. c++ 的 asio 同样支持.

最新 can-utils 安装

can-utils 是 Linux CAN/SocketCAN 的用户空间应用程序. 如常见的 cansend, candump 等

之前的 cangw 对 canfd 的支持并不好, 在新版 can-utils 提供了支持, 可直接从源码编译安装:

git clone https://github.com/linux-can/can-utils.git
cd can-utils
mkdir build && cd build && make -j12 && sudo make install

VCAN 举例

Linux 虚拟网络 的功能异常丰富, 为容器等打下了坚实的技术基础.

VCAN, Virtual CAN, 类似于虚拟的网络 loopback 设备, 在本机测试CAN协议实现时, 可以使用 VCAN.

在这里插入图片描述

实现上图的一个 vcan0 的脚本

#!/bin/bash
# if -c parameter is given, then clean up
if [ "$1" = "-c" ]; then
    sudo ip link set down vcan0
    sudo ip link delete vcan0
    exit 0
fi
sudo modprobe can
sudo modprobe can-raw
sudo modprobe vcan
# if vcan0 is already created, delete it
if [ -e /sys/class/net/vcan0 ]; then
    sudo ip link set down vcan0
    sudo ip link delete vcan0
fi
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

是的, vcan 无需设置通信速率等, 内核跑多快, 速率就有多快, 自带 loopback, 多个 app 可以通过单个 vcan 通信. can 和 canfd 也都支持的不错, 命名也不必 vcanx, 取名 dog, vcanpig 也可以

# 可以多个窗口开多个candump
$ candump -td -x vcan0
 (000.000000)  vcan0  TX - -  123   [2]  11 22
 (025.888347)  vcan0  TX B E  12345678  [12]  11 22 33 44 55 66 77 88 99 00 00 00
 
$ cansend vcan0 123#11.22
$ cansend vcan0 12345678##3.11.22.33.44.55.66.77.88.99

VXCAN 举例

VXCAN, Virtual CAN Tunnel, 与 veth 类似, 实现了 CAN 流量隧道, 创建 VXCAN 时, 两个 VXCAN 设备会成对创建, 可用于跨命名空间通信, 不提供本地回显 loopback. 见于 Linux 4.12 内核以后

在这里插入图片描述

实现上图的一个 vxcan 的脚本

#!/bin/bash

# if -c parameter is given, then clean up
if [ "$1" = "-c" ]; then
    sudo ip link set down vxcan0
    sudo ip link delete vxcan0
    exit 0
fi

sudo modprobe can
sudo modprobe can_raw
sudo modprobe vxcan

sudo ip link add vxcan0 type vxcan peer name vxcan1
sudo ip link set vxcan0 up
sudo ip link set vxcan1 up

说明:

  • 删除一对中的任意一个, 另一个也会消失
  • 默认支持 can 和 canfd

运行后, 可以看到多出一对 vxcan 设备

$ ip link
22: vxcan1@vxcan0: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can 
23: vxcan0@vxcan1: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can 
    
$ candump -td -x any
 (000.000000)  vxcan1  TX B E  123  [12]  11 22 33 44 55 66 77 88 99 00 00 00
 (000.000020)  vxcan0  TX B E  123  [12]  11 22 33 44 55 66 77 88 99 00 00 00
 (008.254112)  vxcan0  TX B E  12345678  [12]  11 22 33 44 55 66 77 88 99 00 00 00
 (000.000021)  vxcan1  TX B E  12345678  [12]  11 22 33 44 55 66 77 88 99 00 00 00
 
$ cansend vxcan0 123##3.11.22.33.44.55.66.77.88.99
$ cansend vxcan1 12345678##3.11.22.33.44.55.66.77.88.99

下面是一个跨命名空间通信的例子

  • 除了 host, 还有 netns1, netns2 两个网络命名空间
  • vxcan0-vxcan1 连接了 netns1, netns2 两个网络命名空间
  • vxcan2-vxcan3 连接了 netns1 和 host
  • app1 既能和 app2/app3 通信, 也能和 app4 通信

在这里插入图片描述

实现上图的脚本

#!/bin/bash

# if -c parameter is given, then clean up
if [ "$1" = "-c" ]; then
    sudo ip netns del ns1
    sudo ip netns del ns2
    # sudo ip link set down vxcan2
    # sudo ip link delete vxcan2
    exit 0
fi

sudo modprobe can
sudo modprobe can_raw
sudo modprobe vxcan

# ns1 - ns2
sudo ip netns add ns1
sudo ip netns add ns2
sudo ip link add vxcan0 netns ns1 type vxcan peer name vxcan1 netns ns2
sudo ip netns exec ns1 ip link set vxcan0 up
sudo ip netns exec ns2 ip link set vxcan1 up

# ns1 - host
sudo ip link add vxcan2 netns ns1 type vxcan peer name vxcan3
sudo ip netns exec ns1 ip link set vxcan2 up
sudo ip link set vxcan3 up

运行后, 可以在各自的命名空间查看

$ ip link
24: vxcan3@if5: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can  link-netns ns1
    
    
$ sudo ip netns exec ns1 ip link
4: vxcan0@if4: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can  link-netns ns2
5: vxcan2@if24: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can  link-netnsid 1
    
$ sudo ip netns exec ns2 ip link
4: vxcan1@if4: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/can  link-netns ns1

收发测试

# ns1,vxcan0 - ns2,vxcan1
$ sudo ip netns exec ns1 candump -td -x any
$ sudo ip netns exec ns2 candump -td -x any
$ sudo ip netns exec ns1 cansend vxcan0 123##3.11.22
$ sudo ip netns exec ns2 cansend vxcan1 123##3.11.23

# ns1,vxcan2 - host,vxccan3
$ sudo ip netns exec ns1 candump -td -x any
$ candump -td -x any
$ sudo ip netns exec ns1 cansend vxcan2 123##3.11.22
$ cansend vxcan3 123##3.11.23

如果嫌前缀 sudo ip netns exec ns1 过长的话, 可以先进入ns1: sudo ip netns exec ns1 bash. 其它命令举例

$ ip netns list
ns2
ns1 (id: 0)

$ sudo ip netns exec ns2 lsns
        NS TYPE   NPROCS    PID USER            COMMAND
4026533123 mnt        10  85584 root            /init
$ sudo nsenter -t 85584 -n bash
# exit 或 Ctrl+D 退出这个bash

CANGW 举例

安装 can-utils 后就可以使用 cangw 命令了

先来查看下 help, 可以看出, 新的 can-utils 中的 cangw 已经支持了 canfd

$ cangw help
cangw - manage PF_CAN netlink gateway.

Usage: cangw [options]

Commands:
          -A  (add a new rule)
          -D  (delete a rule)
          -F  (flush / delete all rules)
          -L  (list all rules)
Mandatory:
          -s <src_dev>  (source netdevice)
          -d <dst_dev>  (destination netdevice)
Options:
          -X  (this is a CAN FD rule)
          -t  (preserve src_dev rx timestamp)
          -e  (echo sent frames - recommended on vcanx)
          -i  (allow to route to incoming interface)
          -u <uid>  (user defined modification identifier)
          -l <hops>  (limit the number of frame hops / routings)
          -f <filter>  (set CAN filter)
          -m <mod>  (set Classical CAN frame modifications)
          -M <MOD>  (set CAN FD frame modifications)
          -x <from_idx>:<to_idx>:<result_idx>:<init_xor_val>  (XOR checksum)
          -c <from>:<to>:<result>:<init_val>:<xor_val>:<crctab[256]>  (CRC8 cs)
          -p <profile>:[<profile_data>]  (CRC8 checksum profile & parameters)

Values are given and expected in hexadecimal values. Leading 0s can be omitted.
...

cangw 可以把 真实的can, vcan, vxcan 连起来, 如

在这里插入图片描述

或者

在这里插入图片描述

此图中 app0 可以和 app2, app3, app4, app5 进行通信, 脚本如下

#!/bin/bash

# if -c parameter is given, then clean up
if [ "$1" = "-c" ]; then
    sudo cangw -F -s vcan0 -d vxcan0
    sudo cangw -F -s vxcan0 -d vcan0
    cangw -L
    sudo ip link set down vxcan0
    sudo ip link delete vxcan0
    sudo ip link set down vcan0
    sudo ip link delete vcan0
    exit 0
fi

sudo modprobe can
sudo modprobe can_raw
sudo modprobe can-gw
sudo modprobe vcan
sudo modprobe vxcan

# vxcan
sudo ip link add vxcan0 type vxcan peer name vxcan1
sudo ip link set vxcan0 up
sudo ip link set vxcan1 up

# vcan
sudo ip link add vcan0 type vcan
sudo ip link set vcan0 up

# cangw
sudo cangw -A -s vcan0 -d vxcan1 -e
sudo cangw -A -s vxcan0 -d vcan0 -e
sudo cangw -A -X -s vcan0 -d vxcan0 -e
sudo cangw -A -X -s vxcan0 -d vcan0 -e

说明:

  • -A, add, 添加规则
  • -s, source, 源
  • -d, destination
  • -e, echo sent frames - recommended on vcanx, 回显
  • -X, CAN FD支持, 这里标准 CAN 和 CAN FD 分开写了, 不知道未来会不会合并

查看 cangw 规则列表

$ cangw -L
cangw -A -s vxcan0 -d vcan0 -e # 0 handled 0 dropped 0 deleted
cangw -A -s vcan0 -d vxcan0 -e # 0 handled 0 dropped 0 deleted
cangw -A -s vxcan0 -d vcan0 -e # 0 handled 0 dropped 0 deleted
cangw -A -s vcan0 -d vxcan1 -e # 0 handled 0 dropped 0 deleted

测试

$ candump -td -x any
 (000.000000)  vxcan1  TX - -  123   [1]  11
 (000.000036)  vxcan0  TX - -  123   [1]  11
 (000.000003)   vcan0  RX - -  123   [1]  11
 (005.983670)  vxcan1  TX B E  123  [02]  11 22
 (000.000039)  vxcan0  TX B E  123  [02]  11 22
 (000.000003)   vcan0  RX B E  123  [02]  11 22
 (011.298185)   vcan0  TX B E  123  [03]  11 22 33
 (000.000041)  vxcan1  RX B E  123  [03]  11 22 33
 (000.000000)  vxcan0  RX B E  123  [03]  11 22 33
 (013.869664)   vcan0  TX - -  123   [4]  11 22 33 44
 (000.000041)  vxcan0  RX - -  123   [4]  11 22 33 44
 (000.000001)  vxcan1  RX - -  123   [4]  11 22 33 44
 
$ cansend vxcan0 123#11
$ cansend vxcan0 123##3.11.22
$ cansend vcan0 123##3.11.22.33
$ cansend vcan0 123#11.22.33.44

参考