hartkopp/can-isotp

Catching missing Flow Control Frame during data transmission

Closed this issue · 1 comments

Sauci commented

This issue was opened after an exchange of emails with @hartkopp

Extract of the e-mail sent to @hartkopp

I had a question concerning the behavior when sending a multi-frame packet. When I (client side) send a large chunk of data to the server (ECU), and the server does not send a Flow Control frame back to the client, the write(2) operation does not return any clue about this misbehaving transaction (no error returned, and the number of bytes sent corresponds to the size of the packet).
I wanted to know if there is a way to know when the client enters in such a state.

I digged into the examples but did not found such a use-case.

extract of the reply from @hartkopp

When there's no FC we run into a timeout here:

https://elixir.bootlin.com/linux/v5.11.10/source/net/can/isotp.c#L751

I wanted to know if there is a way to know when the client enters in
such a state.

Good point. Usually you catch these errors by monitoring for a timeout on application(!) level as ISO-TP is an unreliable datagramm protocol anyway (like UDP).

Btw. there is a 'blocking write' feature with the CAN_ISOTP_WAIT_TX_DONE flag. This only returns from the write call when either the transmission is completed OR there has been a problem (e.g. timeout).

In the 'problem' case the sk_error is set.

But looking at the code here ...

https://elixir.bootlin.com/linux/v5.11.10/source/net/can/isotp.c#L955

... it always seems to return the original length value. And I did not test how the sk_error is propagated to the user.

Hm - maybe we can both take a look at this.

Did you try CAN_ISOTP_WAIT_TX_DONE?

Without CAN_ISOTP_WAIT_TX_DONE the errors can not be assigned to a specific PDU.

Sauci commented

Setting the CAN_ISOTP_WAIT_TX_DONE doesn't indeed affect the return value of write(2). Bellow a sample code illustrating this behavior:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <net/if.h>
#include <linux/can.h>
#include <linux/can/isotp.h>

static const u_int8_t tx_buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int main() {
    int status = 0;

    int fd;
    struct sockaddr_can address;
    static struct can_isotp_options isotp_options;
    static struct can_isotp_ll_options isotp_ll_loptions;

    address.can_family = AF_CAN;
    address.can_addr.tp.rx_id = 0x98DAF900u;
    address.can_addr.tp.tx_id = 0x98DA00F9u;
    address.can_ifindex = (signed) if_nametoindex("can1");

    isotp_options.flags = CAN_ISOTP_DEFAULT_FLAGS | CAN_ISOTP_WAIT_TX_DONE;

    isotp_ll_loptions.mtu = 16u;
    isotp_ll_loptions.tx_dl = 8u;
    isotp_ll_loptions.tx_flags = 0u;

    if ((fd = socket(PF_CAN, SOCK_DGRAM, CAN_ISOTP)) >= 0) {
        if ((setsockopt(fd, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &isotp_options, sizeof(isotp_options))) >= 0) {
            if ((setsockopt(fd, SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, &isotp_ll_loptions, sizeof(isotp_ll_loptions))) >= 0) {
                if ((bind(fd, (struct sockaddr *)&address, sizeof(address))) >= 0) {
                    ssize_t result = write(fd, &tx_buffer, sizeof(tx_buffer) / sizeof(tx_buffer[0]));
                    printf("result = %d", result);
                } else {
                    status = 4;
                }
            } else {
                status = 3;
            }
        } else {
            status = 2;
        }
    } else {
        status = 1;
    }

    return status;
}

This executable outputs:

/home/pi/isotp/cmake-build-debug-rpi/isotp
result = 10
Process finished with exit code 0

While the can dump utility outputs:

$ candump can1
  can1  18DA00F9   [8]  10 08 01 02 03 04 05 06

Should the call to write(2) return a negative value? Or should it return the number of byte effectively written (in this case 6) if the writing operations are configured in blocking mode trough CAN_ISOTP_WAIT_TX_DONE option?