如何使用libnl和通用网络链接发送多部分消息?

时间:2020-06-23 03:02:35

标签: linux-kernel kernel netlink

我正在尝试通过libnl和通用网络链接发送一个较大的字符串(6Kb),但是在此过程中,我从函数nla_put_string收到错误-5(NL_ENOMEM)。我做了很多研究,但没有找到关于这两个问题的任何信息:

  • 通用netlink和libnl nla_put_string函数支持的最大字符串大小是多少?
  • 如何使用通用netlink的多部分机制将此字符串分成较小的部分,以在内核端发送和重新组装?

如果有地方可以学习这样的主题,

1 个答案:

答案 0 :(得分:0)

如何使用通用netlink的多部分机制将此字符串分成较小的部分,以在内核侧发送和重新组装?

Netlink的Multipart功能“可能”可以帮助您传输已经零散的字符串,但是它对实际的字符串零碎操作没有帮助。那是你的工作。 Multipart是一种通过几个数据包而不是一个大对象传输几个小相关对象的方法。通常,整个Netlink的设计假设是要发送的任何原子数据都可以放入单个数据包中。我同意这样的观点,即6Kbs的字符串有点奇怪。

实际上,在我看来,Multipart是一种不太明确的设计。问题在于内核实际上并没有以任何通用能力来处理它。如果您查看所有的NLMSG_DONE usage instances,不仅会发现它很少被读取(大多数是写操作),而且它不是Netlink代码,而是一些specific protocol一些static(即私有)操作。换句话说,NLMSG_DONE的语义是由您而不是内核给出的。如果选择使用Linux,它将不会为您节省任何工作。

另一方面,libnl-genl-3似乎确实对Multipart标志(NLMSG_DONENLM_F_MULTI)进行了一些自动调整,但这仅在您从内核空间发送内容时适用到用户空间,最重要的是,甚至库本身也承认it doesn't really work

此外,NLMSG_DONE应该是placed in the "type" Netlink header field,而不是在“标志”字段中。这让我感到莫名其妙,因为通用Netlink stores the family identifier in type,所以看起来好像没有一种方法可以同时告诉Netlink消息属于您,并且应该终止某些数据流。除非我缺少重要的内容,否则Multipart和Generic Netlink互不兼容。

因此,我建议您在必要时实施自己的消息控件,而不必担心Multipart。

通用netlink和libnl nla_put_string函数支持的最大字符串大小是多少?

这不是一个常数。 nlmsg_alloc()储备 每个数据包getpagesize()by default字节。您可以使用nlmsg_set_default_size()来调整此默认值,或者甚至可以使用nlmsg_alloc_size()来覆盖它。

然后,您必须查询actual allocated size(因为不能保证它是您所要求的)并从那里进行构建。要获得可用的有效负载,您必须减去要添加的任何属性的Netlink header长度,Generic Header长度和Attribute Header长度。还有user header length(如果有)。您还必须对齐所有这些组件,因为它们的sizeof不一定是它们的实际大小(example)。

话虽如此,内核仍然会拒绝超过页面大小的数据包,因此,即使您指定自定义大小,您仍然需要对字符串进行分段。

所以,真的,请忘记以上所有内容。只需将字符串分段成getpagesize() / 2之类的东西,然后将其分开发送即可。

这是一般想法:

static void do_request(struct nl_sock *sk, int fam, char const *string)
{
    struct nl_msg *msg;

    msg = nlmsg_alloc();
    genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, fam,
            0, 0, DOC_EXMPL_C_ECHO, 1);
    nla_put_string(msg, DOC_EXMPL_A_MSG, string);
    nl_send_auto(sk, msg);
    nlmsg_free(msg);
}

int main(int argc, char **argv)
{
    struct nl_sock *sk;
    int fam;

    sk = nl_socket_alloc();
    genl_connect(sk);
    fam = genl_ctrl_resolve(sk, FAMILY_NAME);

    do_request(sk, fam, "I'm sending a string.");
    do_request(sk, fam, "Let's pretend I'm biiiiiig.");
    do_request(sk, fam, "Look at me, I'm so big.");
    do_request(sk, fam, "But I'm already fragmented, so it's ok.");
    
    nl_close(sk);
    nl_socket_free(sk);
    
    return 0;
}

我在my Dropbox中留下了一个完整的沙箱。请参阅自述文件。 (已在5.4.0-37版通用内核中进行测试。)