Skip to content

FC 0x11 (Report Slave ID) Is Incorrectly Handled Over Modbus TCP (Serial-Line-Only Function Exposed) #844

@MrAlaskan

Description

@MrAlaskan

Versions

  • OS: Ubuntu 20.04

  • libmodbus: 3.1.12

libmodbus Specific

  • Server: Modbus TCP server using modbus_new_tcp, receives one request with modbus_receive(), then responds with modbus_reply().

  • Client: Modbus TCP client using modbus_report_slave_id() (FC 0x11) over TCP and prints response bytes.

Description

I tested whether MODBUS_FC_REPORT_SLAVE_ID (FC 0x11, serial-line-only in the Modbus spec) is accepted on a TCP backend.

Observed behavior:

  • A TCP client sends FC 0x11 via modbus_report_slave_id().
  • The TCP server processes the request and returns a valid response payload.
  • The response body contains the LMB marker plus version string bytes, e.g. LMB3.1.12.

What went wrong:

  • FC 0x11 is specified as serial-line-only, but it is still handled over TCP.

Expected behavior:

  • On TCP backend, FC 0x11 should be rejected (for example with illegal function) or otherwise disabled by default.

Code and Logs

/* Server */
#include <errno.h>
#include <modbus.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef _WIN32
#include <winsock2.h>
#else
#include <unistd.h>
#endif

int main(int argc, char *argv[])
{
    const char *bind_ip = (argc > 1) ? argv[1] : "0.0.0.0";
    int port = (argc > 2) ? atoi(argv[2]) : 1502;
    modbus_t *ctx = NULL;
    modbus_mapping_t *mapping = NULL;
    int listen_fd = -1;
    uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
    int rc;

    ctx = modbus_new_tcp(bind_ip, port);
    if (ctx == NULL) {
        fprintf(stderr, "modbus_new_tcp failed\n");
        return 1;
    }
    modbus_set_debug(ctx, 1);
    listen_fd = modbus_tcp_listen(ctx, 1);
    if (listen_fd == -1) {
        fprintf(stderr, "modbus_tcp_listen failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return 1;
    }

    if (modbus_tcp_accept(ctx, &listen_fd) == -1) {
        fprintf(stderr, "modbus_tcp_accept failed: %s\n", modbus_strerror(errno));
#ifndef _WIN32
        close(listen_fd);
#endif
        modbus_free(ctx);
        return 1;
    }

    mapping = modbus_mapping_new(1, 1, 1, 1);
    if (mapping == NULL) {
        fprintf(stderr, "modbus_mapping_new failed: %s\n", modbus_strerror(errno));
        modbus_close(ctx);
#ifndef _WIN32
        close(listen_fd);
#endif
        modbus_free(ctx);
        return 1;
    }

    printf("server started ip=%s port=%d\n", bind_ip, port);

    rc = modbus_receive(ctx, req);
    if (rc <= 0) {
        fprintf(stderr, "modbus_receive failed: %s\n", modbus_strerror(errno));
        modbus_mapping_free(mapping);
        modbus_close(ctx);
#ifndef _WIN32
        close(listen_fd);
#endif
        modbus_free(ctx);
        return 1;
    }

    printf("server got request_length=%d function=0x%02X\n", rc, req[7]);
    if (modbus_reply(ctx, req, rc, mapping) == -1) {
        fprintf(stderr, "modbus_reply failed: %s\n", modbus_strerror(errno));
        modbus_mapping_free(mapping);
        modbus_close(ctx);
#ifndef _WIN32
        close(listen_fd);
#endif
        modbus_free(ctx);
        return 1;
    }
    printf("server replied to FC 0x11 over TCP\n");

    modbus_mapping_free(mapping);
    modbus_close(ctx);
#ifndef _WIN32
    close(listen_fd);
#endif
    modbus_free(ctx);
    return 0;
}
/* Client */
#include <errno.h>
#include <modbus.h>
#include <stdio.h>
#include <stdlib.h>

static void print_hex(const uint8_t *buf, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        printf("%02X", buf[i]);
        if (i + 1 < len) {
            printf(" ");
        }
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    const char *ip = (argc > 1) ? argv[1] : "127.0.0.1";
    int port = (argc > 2) ? atoi(argv[2]) : 1502;
    modbus_t *ctx = NULL;
    uint8_t report[MODBUS_MAX_PDU_LENGTH];
    int rc;
    int i;
    int found_lmb = 0;

    ctx = modbus_new_tcp(ip, port);
    if (ctx == NULL) {
        fprintf(stderr, "modbus_new_tcp failed\n");
        return 1;
    }
    modbus_set_debug(ctx, 1);
    if (modbus_set_slave(ctx, 1) == -1) {
        fprintf(stderr, "modbus_set_slave failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return 1;
    }

    if (modbus_connect(ctx) == -1) {
        fprintf(stderr, "modbus_connect failed: %s\n", modbus_strerror(errno));
        modbus_free(ctx);
        return 1;
    }

    printf("client started ip=%s port=%d\n", ip, port);
    printf("sending FC0x11 via modbus_report_slave_id over TCP\n");
    rc = modbus_report_slave_id(ctx, MODBUS_MAX_PDU_LENGTH, report);
    if (rc == -1) {
        fprintf(stderr, "modbus_report_slave_id failed: %s\n", modbus_strerror(errno));
        modbus_close(ctx);
        modbus_free(ctx);
        return 1;
    }

    printf("report length=%d\n", rc);
    print_hex(report, rc);
    if (rc > 1) {
        printf("run_indicator_status=0x%02X\n", report[1]);
    }

    for (i = 0; i + 2 < rc; i++) {
        if (report[i] == 'L' && report[i + 1] == 'M' && report[i + 2] == 'B') {
            found_lmb = 1;
            printf("fingerprint detected at offset=%d\n", i);
            break;
        }
    }
    if (!found_lmb) {
        printf("no LMB fingerprint found in response body\n");
    }

    modbus_close(ctx);
    modbus_free(ctx);
    return 0;
}
$ ./server
Client connection accepted from 127.0.0.1.
server started ip=0.0.0.0 port=1502
Waiting for an indication...
<00><01><00><00><00><02><01><11>
server got request_length=8 function=0x11
[00][01][00][00][00][0E][01][11][0B][B4][FF][4C][4D][42][33][2E][31][2E][31][32]
server replied to FC 0x11 over TCP

$ ./client
Connecting to 127.0.0.1:1502
client started ip=127.0.0.1 port=1502
sending FC0x11 via modbus_report_slave_id over TCP
[00][01][00][00][00][02][01][11]
Waiting for a confirmation...
<00><01><00><00><00><0E><01><11><0B><B4><FF><4C><4D><42><33><2E><31><2E><31><32>
report length=11
B4 FF 4C 4D 42 33 2E 31 2E 31 32
run_indicator_status=0xFF
fingerprint detected at offset=2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions