1

I am trying to use nl_recvmsgs as a blocking function to receive Netlink messages from a kernel module. In my example the client sends a message to the kernel and then it calls nl_recvmsgs_report()(is equal to nl_recvmsgs). Then the kernel module sends a return message. That message is successful received by the client.

Now I want the client to listen for more messages in the future and call nl_recvmsgs_report() again. The kernel did not sent any second message. But somehow an ERRORMSG is received by the client. This leads to a SEGFAULT at the client because he tries to parse the message not as an ERRORMSG.

If I check if the message type is 2 , and skip the message parsing, a third call of nl_recvmsgs_report() blocks perfectly fine.

Does someone know why the client receives this ERRORMSG?


Have a look at my github branch. Just call make, sudo insmod nlk.ko, ./nlclient I copied only the relevant parts here.

client code

nlclient.c main() sending and receiving part:

  // setup netlink socket
  sk = nl_socket_alloc();
  nl_socket_disable_seq_check(sk);  // disable sequence number check
  genl_connect(sk);

  int id = genl_ctrl_resolve(sk, DEMO_FAMILY_NAME);

  struct nl_msg * msg;


  // create a messgae
  msg = nlmsg_alloc();
  genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, id, 0,    // hdrlen
                        0,  // flags
                        DEMO_CMD,   // numeric command identifier
                        DEMO_VERSION    // interface version
                       );

  nla_put_string(msg, DEMO_ATTR1_STRING, "hola");
  nla_put_u16(msg, DEMO_ATTR2_UINT16, 0xf1);

  // send it
  nl_send_auto(sk, msg);

  // handle reply
  struct nl_cb * cb = NULL;
  cb = nl_cb_alloc(NL_CB_CUSTOM);

  //nl_cb_set_all(cb, NL_CB_DEBUG, NULL, NULL);
  nl_cb_set_all(cb, NL_CB_CUSTOM, cb_handler, &cbarg);
  nl_cb_err(cb, NL_CB_DEBUG, NULL, NULL);

  int nrecv = nl_recvmsgs_report(sk, cb);

  printf("cbarg %d nrecv %d\n", cbarg, nrecv);

  printf("First test if it blocks here for incoming messages:\n");
  nrecv = nl_recvmsgs_report(sk, cb);

  printf("cbarg %d nrecv %d\n", cbarg, nrecv);

  printf("Second test if it blocks here for incoming messages:\n");
  nrecv = nl_recvmsgs_report(sk, cb);

  printf("cbarg %d nrecv %d\n", cbarg, nrecv);

nlclient.c cb_handler() parsing the header and message

  struct nlmsghdr * hdr = nlmsg_hdr(msg);

  struct genlmsghdr * gnlh = nlmsg_data(hdr);

  nl_msg_dump(msg, stderr);

  if (hdr->nlmsg_type == 2) {
    printf("hdr->nlmsg_type is ERROR. Skipping message parsing!\n");    
  } else {

    int valid =
      genlmsg_validate(hdr, 0, DEMO_ATTR_MAX, demo_gnl_policy);
    printf("valid %d %s\n", valid, valid ? "ERROR" : "OK");

    // one way
    struct nlattr * attrs[DEMO_ATTR_MAX + 1];

    if (genlmsg_parse(hdr, 0, attrs, DEMO_ATTR_MAX, demo_gnl_policy) < 0)
      {
        printf("genlsmg_parse ERROR\n");
      }

    else
      {
        printf("genlsmg_parse OK\n");

        printf("attr1 %s\n", nla_get_string(attrs[DEMO_ATTR1_STRING]));
        printf("attr2 %x\n", nla_get_u16(attrs[DEMO_ATTR2_UINT16]));
        struct attr_custom * cp = (struct attr_custom *) nla_data(attrs[DEMO_ATTR3_CUSTOM]);
        printf("attr3 %d %ld %f %lf\n", cp->a, cp->b, cp->c,cp->d);

      }
    }
  // another way
  printf("gnlh->cmd %d\n", gnlh->cmd);  //--- DEMO_CMD_ECHO

  int remaining = genlmsg_attrlen(gnlh, 0);
  struct nlattr * attr = genlmsg_attrdata(gnlh, 0);

  while (nla_ok(attr, remaining))
    {
      printf("remaining %d\n", remaining);
      printf("attr @ %p\n", attr); // nla_get_string(attr)
      attr = nla_next(attr, &remaining);
    }

kernel code

nlkernel.c demo_cmd() sending to client part:

/* send message back */
    /* allocate some memory, since the size is not yet known use NLMSG_GOODSIZE */
    skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
    if (skb == NULL) {
        goto out;
    }

    /* create the message */
    msg_head =
        genlmsg_put(skb, 0, info->snd_seq + 1, &demo_gnl_family, 0,
            DEMO_CMD);

    if (msg_head == NULL) {
        rc = -ENOMEM;
        goto out;
    }

    rc |= nla_put_string(skb, DEMO_ATTR1_STRING,"world");
    rc |= nla_put_u16(skb, DEMO_ATTR2_UINT16, 0x1f);
    cp.a = 1;
    cp.b = 2;
    cp.c = 3.0;
    cp.d = 4.0;
    rc |= nla_put(skb, DEMO_ATTR3_CUSTOM, sizeof(struct attr_custom), &cp);

    if (rc != 0) {
        goto out;
    }

    /* finalize the message */
    genlmsg_end(skb, msg_head);

    /* send the message back */
    rc = genlmsg_unicast(&init_net, skb, info->snd_portid);

    if (rc != 0) {
        goto out;
    }

    return 0;

output

nlclient console output

./nlclient 
--------------------------   BEGIN NETLINK MESSAGE ---------------------------
  [NETLINK HEADER] 16 octets
    .nlmsg_len = 76
    .type = 27 <0x1b>
    .flags = 0 <>
    .seq = 1458476257
    .port = 0
  [GENERIC NETLINK HEADER] 4 octets
    .cmd = 1
    .version = 1
    .unused = 0
  [PAYLOAD] 56 octets
    0a 00 01 00 77 6f 72 6c 64 00 00 00 06 00 02 00 ....world.......
    1f 00 00 00 24 00 03 00 01 00 00 00 ff ff ff ff ....$...........
    02 00 00 00 00 00 00 00 00 00 40 40 04 88 ff ff ..........@@....
    00 00 00 00 00 00 10 40                         .......@
---------------------------  END NETLINK MESSAGE   ---------------------------
valid 0 OK
genlsmg_parse OK
attr1 world
attr2 1f
attr3 1 2 3.000000 4.000000
gnlh->cmd 1
remaining 56
attr @ 0x10df344
remaining 44
attr @ 0x10df350
remaining 36
attr @ 0x10df358
cbarg 123 nrecv 1
First test if it blocks here for incoming messages:
--------------------------   BEGIN NETLINK MESSAGE ---------------------------
  [NETLINK HEADER] 16 octets
    .nlmsg_len = 36
    .type = 2 <ERROR>
    .flags = 0 <>
    .seq = 1458476256
    .port = -1061151077
  [ERRORMSG] 20 octets
    .error = 0 "Success"
  [ORIGINAL MESSAGE] 16 octets
    .nlmsg_len = 16
    .type = 27 <0x1b>
    .flags = 5 <REQUEST,ACK>
    .seq = 1458476256
    .port = -1061151077
---------------------------  END NETLINK MESSAGE   ---------------------------
hdr->nlmsg_type is ERROR. Skipping message parsing!
gnlh->cmd 0
cbarg 123 nrecv 1
Second test if it blocks here for incoming messages:

KERNEL syslog

kernel: [ 4694.318428] got demo_cmd
kernel: [ 4694.318430] attr1: hola
kernel: [ 4694.318431] attr2: f1
aykes
  • 13
  • 4

1 Answers1

1

Sorry for taking so long.

The output is somewhat misleading. That's not an error message; it's an automatic ACK. Netlink defines ACKs to be "error" messages with error code 0.

(Zero is typical jargon for success in the C language.)

Since you're crafting an answer, you probably don't need ACKs anyway. You can stop your client from requesting ACKs by adding a call to nl_socket_disable_auto_ack().

I'd hack it close to sequence check disabling because it's kind of similar:

sk = nl_socket_alloc();
nl_socket_disable_seq_check(sk);
nl_socket_disable_auto_ack(sk);
genl_connect(sk);
Yd Ahhrk
  • 1,088
  • 12
  • 24