CAN核心RAW协议

本文介绍了CAN核心中的RAW协议,详细阐述了sock对象raw_sock的数据结构以及PF_CAN协议族的地址类型sockaddr_can。重点讲解了初始化过程,包括raw_init()函数,以及socket接口的实现,如绑定、设置过滤器、发送和接收数据的细节。特别指出,设置过滤器的关键在于raw_setsockopt()函数,并且接收数据是通过CAN核心调用raw_rcv()完成的。
摘要由CSDN通过智能技术生成

数据结构

sock对象raw_sock

struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* data length code: 0 .. 8 */
    __u8    data[8] __attribute__((aligned(8)));
};

/*
 * A raw socket has a list of can_filters attached to it, each receiving
 * the CAN frames matching that filter.  If the filter list is empty,
 * no CAN frames will be received by the socket.  The default after
 * opening the socket, is to have one filter which receives all frames.
 * The filter list is allocated dynamically with the exception of the
 * list containing only one item.  This common case is optimized by
 * storing the single filter in dfilter, to avoid using dynamic memory.
 */
struct raw_sock {
    struct sock sk;
    int bound; // 标识是否执行过bind()
    int ifindex; // 记录bind()的网络设备索引
    struct notifier_block notifier;
    int loopback; // 标识是否让发送的数据帧环回
    int recv_own_msgs; // 1:可接收本端发送报文;0:拒绝接收本端发送报文
    int count;                 /* number of active filters */
    struct can_filter dfilter; /* default/single filter */
    /* pointer to filter(s),如果只有1个filter,那么filter会只想dfilter */
    struct can_filter *filter;
    can_err_mask_t err_mask;
};

PF_CAN协议族地址: sockaddr_can

和IP协议族类似,CAN协议族对应的地址为socket地址为sockaddr_can。

/**
 * struct sockaddr_can - the sockaddr structure for CAN sockets
 * @can_family:  address family number AF_CAN.
 * @can_ifindex: CAN network interface index.
 * @can_addr:    protocol specific address information
 */
struct sockaddr_can {
    sa_family_t can_family;
    int         can_ifindex;
    union {
        /* transport protocol class address information (e.g. ISOTP) */
        struct { canid_t rx_id, tx_id; } tp;
        /* reserved for future CAN protocols address information */
    } can_addr;
};

初始化

// 后面重点关注bind()、setsockopt()、getsockopt()、sendmsg()、recvmsg()的实现
static struct proto_ops raw_ops __read_mostly = {
    .family        = PF_CAN,
    .release       = raw_release,
    .bind          = raw_bind,
    .connect       = sock_no_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = sock_no_accept,
    .getname       = raw_getname, // 只支持获取本端地址信息
    .poll          = datagram_poll,
    .ioctl         = NULL,		/* use can_ioctl() from af_can.c */
    .listen        = sock_no_listen,
    .shutdown      = sock_no_shutdown,
    .setsockopt    = raw_setsockopt,
    .getsockopt    = raw_getsockopt,
    .sendmsg       = raw_sendmsg,
    .recvmsg       = raw_recvmsg,
    .mmap          = sock_no_mmap,
    .sendpage      = sock_no_sendpage,
};

static struct proto raw_proto __read_mostly = {
    .name       = "CAN_RAW",
    .owner      = THIS_MODULE,
    // 真实的sock对象
    .obj_size   = sizeof(struct raw_sock),
    // socket创建时,由CAN核心调用该协议相关初始化函数初始化sock结构
    .init       = raw_init,
};

// 用户态需要使用socket(PF_CAN, SOCK_RAW, CAN_RAW)创建RAW类型套接字
static struct can_proto raw_can_proto __read_mostly = {
    .type       = SOCK_RAW,
    .protocol   = CAN_RAW,
    .capability = -1,
    .ops        = &raw_ops,
    .prot       = &raw_proto,
};

static __init int raw_module_init(void)
{
    int err;

    printk(banner);
    // 向CAN核心注册RAW类型的协议
    err = can_proto_register(&raw_can_proto);
    if (err < 0)
        printk(KERN_ERR "can: registration of raw protocol failed\n");
    return err;
}

module_init(raw_module_init);

raw_sock初始化: raw_init()

static int raw_init(struct sock *sk)
{
    struct raw_sock *ro = raw_sk(sk);

    ro->bound            = 0;
    ro->ifindex          = 0;

    /* set default filter to single entry dfilter */
    ro->dfilter.can_id   = 0;
    ro->dfilter.can_mask = MASK_ALL;
    ro->filter           = &ro->dfilter;
    ro->count            = 1;

    /* set default loopback behaviour */
    ro->loopback         = 1;
    ro->recv_own_msgs    = 0;

    // 监听了网络设备的去注册和down事件,主要是做一些清理性的工作,这里不再展开
    ro->notifier.notifier_call = raw_notifier;
    register_netdevice_notifier(&ro->notifier);
    return 0;
}

socket接口实现

绑定: raw_bind()

static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
{
    struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
    struct sock *sk = sock->sk;
    struct raw_sock *ro = raw_sk(sk);
    int ifindex;
    int err = 0;
    int notify_enetdown = 0;

    // 地址长度校验
    if (len < sizeof(*addr))
        return -EINVAL;

    lock_sock(sk);
    // 重复绑定
    if (ro->bound && addr->can_ifindex == ro->ifindex)
        goto out;

    if (addr->can_ifindex) { // 要绑定到指定的网络设备
        struct net_device *dev;
        dev = dev_get_by_index(&init_net, addr->can_ifindex);
        if (!dev) {
            err = -ENODEV;
            goto out;
        }
        // 要绑定的网络设备必须是CAN类型
        if (dev->type != ARPHRD_CAN) {
            dev_put(dev);
            err = -ENODEV;
            goto out;
        }
        // 如果当前网络设备没有UP,会主动向用户态报告一次错误
        if (!(dev->flags & IFF_UP))
            notify_enetdown = 1;
        // 记录绑定网络设备索引并设置过滤器
        ifindex = dev->ifindex;
        err = raw_enable_allfilters(dev, sk);
        dev_put(dev);
    } else {
        // 记录绑定网络设备索引=0并设置过滤器
        ifindex = 0;
        err = raw_enable_allfilters(NULL, sk);
    }

    if (!err) {
        if (ro->bound) {
            // 去注册旧的过滤器
            if (ro->ifindex) {
                struct net_device *dev;

                dev = dev_get_by_index(&init_net, ro->ifindex);
                if (dev) {
                    raw_disable_allfilters(dev, sk);
                    dev_put(dev);
                }
            } else
                raw_disable_allfilters(NULL, sk);
        }
        // 记录绑定信息
        ro->ifindex = ifindex;
        ro->bound = 1;
    }

out:
    release_sock(sk);
    if (notify_enetdown) {
        sk->sk_err = ENETDOWN;
        if (!sock_flag(sk, SOCK_DEAD))
            sk->sk_error_report(sk);
    }
    return err;
}

bind()过程最重要的就是设置过滤器,然后将绑定信息更新到raw_sock的ifindex和bound字段中。

使能过滤器: raw_enable_allfilters()

static int raw_enable_filters(struct net_device *dev, struct sock *sk,
			      struct can_filter *filter, int count)
{
    int err = 0;
    int i;

    // 遍历当前已设置的过滤器,分别向CAN核心注册接收者
    for (i = 0; i < count; i++) {
        err = can_rx_register(dev, filter[i].can_id, filter[i].can_mask, raw_rcv, sk, "raw");
        // 错误回滚
        if (err) {
            /* clean up successfully registered filters */
            while (--i >= 0)
                can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask, raw_rcv, sk);
            break;
        }
    }
    return err;
}

static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
				can_err_mask_t err_mask)
{
    int err = 0;

    // 注册错误接收者
    if (err_mask)
        err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG, raw_rcv, sk, "raw");
    return err;
}

static int raw_enable_allfilters(struct net_device *dev, struct sock *sk)
{
    struct raw_sock *ro = raw_sk(sk);
    int err;

    // 分别设置filter和err filter
    err = raw_enable_filters(dev, sk, ro->filter, ro->count);
    if (!err) {
        err = raw_enable_errfilter(dev, sk, ro->err_mask);
        if (err)
            raw_disable_filters(dev, sk, ro->filter, ro->count);
    }
    return err;
}

从filter的使能实现上看,如果要实现bind()过程中能够设置有效的filter,那么必须提前将filter参数设置到内核,从socket接口实现上看,只有setsockopt()可以胜任这一工作了。

设置socket选项: raw_setsockopt()

static int raw_setsockopt(struct socket *sock, int level, int optname,
			  char __user *optval, int optlen)
{
    struct sock *sk = sock->sk;
    struct raw_sock *ro = raw_sk(sk);
    struct can_filter *filter = NULL;  /* dyn. alloc'ed filters */
    struct can_filter sfilter;         /* single filter */
    struct net_device *dev = NULL;
    can_err_mask_t err_mask = 0;
    int count = 0;
    int err = 0;

    // 常规参数校验
    if (level != SOL_CAN_RAW)
        return -EINVAL;
    if (optlen < 0)
        return -EINVAL;

    switch (optname) {
    case CAN_RAW_FILTER: // 配置filter
        // 可一次配置多个filter,单个filter保存到sfilter中,多个filter保存到堆区
        if (optlen % sizeof(struct can_filter) != 0)
            return -EINVAL;
        count = optlen / sizeof(struct can_filter);
        if (count > 1) {
            /* filter does not fit into dfilter => alloc space */
            filter = kmalloc(optlen, GFP_KERNEL);
            if (!filter)
                return -ENOMEM;
            if (copy_from_user(filter, optval, optlen)) {
                kfree(filter);
                return -EFAULT;
            }
        } else if (count == 1) {
            if (copy_from_user(&sfilter, optval, optlen))
                return -EFAULT;
        }

        lock_sock(sk);
        // 如果之前已经绑定过,那么先移除旧的filter
        if (ro->bound && ro->ifindex)
            dev = dev_get_by_index(&init_net, ro->ifindex);
        if (ro->bound) {
            /* (try to) register the new filters */
            if (count == 1)
                err = raw_enable_filters(dev, sk, &sfilter, 1);
            else
                err = raw_enable_filters(dev, sk, filter, count);
            if (err) {
                if (count > 1)
                    kfree(filter);
                goto out_fil;
            }
            /* remove old filter registrations */
            raw_disable_filters(dev, sk, ro->filter, ro->count);
        }
        /* remove old filter space */
        if (ro->count > 1)
            kfree(ro->filter);

        // 将新的filter关联到socket
        if (count == 1) {
            /* copy filter data for single filter */
            ro->dfilter = sfilter;
            filter = &ro->dfilter;
        }
        ro->filter = filter;
        ro->count  = count;

    out_fil:
        if (dev)
            dev_put(dev);
        release_sock(sk);
        break;
    case CAN_RAW_ERR_FILTER: // 配置错误filter
        // 配置的是err_mask
        if (optlen != sizeof(err_mask))
            return -EINVAL;
        if (copy_from_user(&err_mask, optval, optlen))
            return -EFAULT;
        err_mask &= CAN_ERR_MASK;

        lock_sock(sk);
        // 如果之前绑定过,先移除旧的err_mask
        if (ro->bound && ro->ifindex)
            dev = dev_get_by_index(&init_net, ro->ifindex);
        if (ro->bound) {
            /* (try to) register the new err_mask */
            err = raw_enable_errfilter(dev, sk, err_mask);
            if (err)
                goto out_err;
            /* remove old err_mask registration */
            raw_disable_errfilter(dev, sk, ro->err_mask);
        }
        // 保存新的err_mask
        ro->err_mask = err_mask;

    out_err:
        if (dev)
            dev_put(dev);
        release_sock(sk);
        break;
    case CAN_RAW_LOOPBACK: // 配置loopback
        if (optlen != sizeof(ro->loopback))
            return -EINVAL;
        if (copy_from_user(&ro->loopback, optval, optlen))
            return -EFAULT;
        break;
    case CAN_RAW_RECV_OWN_MSGS: // 配置是否接收自身发送报文
        if (optlen != sizeof(ro->recv_own_msgs))
            return -EINVAL;
        if (copy_from_user(&ro->recv_own_msgs, optval, optlen))
            return -EFAULT;
        break;
    default:
        return -ENOPROTOOPT;
    }
    return err;
}

getsockopt()的实现raw_getsockopt()与raw_setsockopt()是一一对应的,这里不再展开。

发送数据: raw_sendmsg()

static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
		       struct msghdr *msg, size_t size)
{
    struct sock *sk = sock->sk;
    struct raw_sock *ro = raw_sk(sk);
    struct sk_buff *skb;
    struct net_device *dev;
    int ifindex;
    int err;

    // 出口设备必须指定,要么提前bind(),要么通过sendmsg()调用指定,二者优先使用sendmsg()的参数
    if (msg->msg_name) {
        struct sockaddr_can *addr = (struct sockaddr_can *)msg->msg_name;
        if (addr->can_family != AF_CAN)
            return -EINVAL;
        ifindex = addr->can_ifindex;
    } else
        ifindex = ro->ifindex;

    // 发送数据长度校验,每次只能发送一个CAN帧
    if (size != sizeof(struct can_frame))
        return -EINVAL;
    // 出口网络设备有效性校验
    dev = dev_get_by_index(&init_net, ifindex);
    if (!dev)
        return -ENXIO;

    // 构造skb,并将要发送的数据填充到skb中
    skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err);
    if (!skb)
        goto put_dev;
    err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
    if (err < 0)
        goto free_skb;

    // 绑定出口设备信息到skb
    skb->dev = dev;
    skb->sk  = sk;

    // 调用CAN核心接口发送CAN帧
    err = can_send(skb, ro->loopback);

    dev_put(dev);
    if (err)
        goto send_failed;
    return size;

free_skb:
    kfree_skb(skb);
put_dev:
    dev_put(dev);
send_failed:
    return err;
}

接收数据: raw_recvmsg()

static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
		       struct msghdr *msg, size_t size, int flags)
{
    struct sock *sk = sock->sk;
    struct sk_buff *skb;
    int err = 0;
    int noblock;

    noblock =  flags & MSG_DONTWAIT;
    flags   &= ~MSG_DONTWAIT;

    // 调用通用的接收接口,该接口可能会阻塞
    skb = skb_recv_datagram(sk, flags, noblock, &err);
    if (!skb)
        return err;

    // 拷贝数据
    if (size < skb->len)
        msg->msg_flags |= MSG_TRUNC;
    else
        size = skb->len;
    err = memcpy_toiovec(msg->msg_iov, skb->data, size);
    if (err < 0) {
        skb_free_datagram(sk, skb);
        return err;
    }
    // 更新sock对象的接收时间戳,因此SIOCGSTAMP选项才可以获取到数据
    sock_recv_timestamp(msg, sk, skb);

    // 拷贝接收网络设备地址
    if (msg->msg_name) {
        msg->msg_namelen = sizeof(struct sockaddr_can);
        memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
    }
    // 释放skb
    skb_free_datagram(sk, skb);
    return size;
}

raw_recvmsg()仅仅是从sock对象的接收队列中取数据,往队列中放数据是由CAN核心调用raw_rcv()完成的,该函数在使能过滤器时通过can_rx_register()注册给了CAN核心。

raw_rcv()

// data指针指向所属sock对象
static void raw_rcv(struct sk_buff *skb, void *data)
{
    struct sock *sk = (struct sock *)data;
    struct raw_sock *ro = raw_sk(sk);
    struct sockaddr_can *addr;

    // 如果没有打开接收自身发送报文开关,则忽略自身的报文
    if (!ro->recv_own_msgs && skb->sk == sk)
        return;

    /* clone the given skb to be able to enqueue it into the rcv queue */
    skb = skb_clone(skb, GFP_ATOMIC);
    if (!skb)
        return;

    /*
     *  Put the datagram to the queue so that raw_recvmsg() can
     *  get it from there.  We need to pass the interface index to
     *  raw_recvmsg().  We pass a whole struct sockaddr_can in skb->cb
     *  containing the interface index.
     */
    // skb->cb设置为入口网络设备的地址
    BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct sockaddr_can));
    addr = (struct sockaddr_can *)skb->cb;
    memset(addr, 0, sizeof(*addr));
    addr->can_family  = AF_CAN;
    addr->can_ifindex = skb->dev->ifindex;

    // 将skb放入sock对象的接收队列
    if (sock_queue_rcv_skb(sk, skb) < 0)
        kfree_skb(skb);
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值