linux-can / can-utils

Linux-CAN / SocketCAN user space applications
2.36k stars 707 forks source link

J1939 - sendto failed: Cannot assign requested address #557

Open alisonbelow opened 1 month ago

alisonbelow commented 1 month ago

Looking for some assistance here - I am trying to create a simple program to send a j1939 message on a vcan interface.

My goals:

int main() {
    int fd = socket(PF_CAN, SOCK_DGRAM, CAN_J1939);
    if (fd < 0) {
        int err = errno;
        std::cerr << "Failed to create socket: " << strerror(err) << " (errno: " << err << ")" << std::endl;
        throw std::runtime_error("Failed to open CAN socket");
    }

    struct sockaddr_can addr;
    memset(&addr, 0, sizeof(addr));
    addr.can_ifindex = if_nametoindex("vcan0");
    addr.can_addr.j1939.name = 0x01;    // peer, name = 1
    addr.can_addr.j1939.addr = 0x80;    // peer, address = 128
    addr.can_addr.j1939.pgn = 0xFFD0;
    addr.can_family = AF_CAN;  

    if (bind(fd, (const struct sockaddr*)&addr, sizeof(sockaddr_can)) < 0) {
        perror ("bind failed");
        close(fd);
        return 1;
    }

    struct sockaddr_can peer;
    memset(&peer, 0, sizeof(peer));
    peer.can_ifindex = if_nametoindex("vcan0");
    peer.can_addr.j1939.name = J1939_NO_NAME;
    peer.can_addr.j1939.addr = 0x41;
    peer.can_addr.j1939.pgn = 0xFFD0;
    peer.can_family = AF_CAN;

    struct can_frame frame;
    memset(&frame, 0, sizeof(frame));
    frame.can_id = 0xFFD0;
    frame.can_dlc = 8;
    std::memset(frame.data, 0xAA, sizeof(frame.data));

    if (sendto(fd, frame.data, sizeof(frame.data), 0, reinterpret_cast<const sockaddr*>(&peer), sizeof(sockaddr_can)) < 0) {
        perror ("send failed");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}

When I run this binary, I get error "sendto failed: Cannot assign requested address".

Is there anything distinctly I'm doing wrong here? I tried to find the configured addressing/names but I do not have a /proc/net/can/j1939 file to cat.

I've confirmed the vcan interface is configured and up:

sudo ip -details link show vcan0
5: vcan0: <NOARP,UP,LOWER_UP> mtu 72 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/can  promiscuity 0 minmtu 0 maxmtu 0 
    vcan numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 

and sudo modprobe vcan is successful.

olerem commented 1 month ago

Hi @alisonbelow ,

You are setting NAME for the bind address: addr.can_addr.j1939.name = 0x01; // peer, name = 1

If you are doing it, you need to do address claiming and the kernel stack should see the address claiming on the bus. So, you should use j1939acd to claim the address, or add own code to do it, or set J1939_NO_NAME. Especially if you areldy set the address, name is not needed. Except of this, you are trying to send a can frame as payload, this makes no sense. Payload is just a payload, everything else is done by the kernel stack. For example if it is more then 8 byte, it will be split and transferred by using transport protocol.