在内核模块中将网络字节序的IP转换为主机字节序的ip4格式

3
我是一名有用的助手,可以为您翻译文本。
我正在使用语言在Arch Linux中编写内核模块。我想将网络IP转换为ip4格式的主机IP:127.0.0.1。
我知道在用户程序中可以使用以下函数:
inetntoa()
ntohs()
ntohl()

我尝试包含socket.hin.h等头文件,并使用下面的函数,但它们都对我无效。
因此,在内核模块中,我无法访问这些函数。是否有替代这些函数的内核模块?
2个回答

4
你可以使用ntohl()等函数。只需添加#include <linux/byteorder/generic.h>头文件即可。 像平常一样使用它:
__le32 le_ipaddr = ntohl(be_ipaddr); /* to flip big-endian IP to little-endian */

此外,通过特定的格式说明符%pI4,您可以轻松地将IPv4地址按照自己的要求打印出来,例如在printk()中,可以这样使用:
__be32 ipaddr /*= gain from somewhere IP in network byte order (__be32 means big endian)*/;
printk(KERN_INFO "Got IP: %pI4\n", &ipaddr); /* in network byte order */
printk(KERN_INFO "Got IP: %pI4h\n", &ipaddr); /* in host byte order */

阅读也可以:

从sk_buff获取IP地址

如何正确使用printk格式说明符(来自Kernel.org):

按引用传递。

IPv4地址

==============

::

%pI4 1.2.3.4

%pi4 001.002.003.004

...

额外的hnbl说明符用于指定主机、网络、大端或小端顺序地址。如果未提供说明符,则使用默认的网络/大端顺序。

...

按引用传递。

IPv6地址

==============

::

%pI6 0001:0002:0003:0004:0005:0006:0007:0008

%pi6 00010002000300040005000600070008

%pI6c 1:2:3:4:5:6:7:8


P.S. 您可以在Linux内核源代码中搜索所需的函数,例如在此网站上:https://elixir.bootlin.com/linux/latest/ident/


-1
如果你所说的“网络IP”是指一个32位大端整数,代表着你想要转换成点分十进制字符串表示的IPv4地址,那么可以使用sprintf()函数来完成。
int32_t ipv4 = 1234567890; // random example

unsigned char *ptr = (unsigned char *)&ipv4;
char ipstr[16];
sprintf(ipstr, "%d.%d.%d.%d", ptr[0], ptr[1],  ptr[2],  ptr[3]);

ipstr 将包含 IP 地址的字符串。


1
@Ctx网络在不同架构上都是大端字节序,因此不存在风险。这就是为什么存在函数htons()htonl()ntohs()ntohl(),它们可以在"主机字节序"和"网络字节序"之间进行转换,其中网络字节序是大端字节序。如果主机已经是大端字节序,则这些函数不会执行任何操作。 - Havenard
这是因为该数字来自用户界面,必须使用htonl()将其转换为网络格式,以便对套接字库有意义。在套接字库中它始终是大端序的! - Havenard
1
字节序是CPU架构的一个特性,而不是库的特性。您正在混淆事情,并用不精确的术语使自己或他人感到困惑。术语“32位大端整数”完全具有误导性,整数既不是大端也不是小端,但可以以大端或小端的方式存储在内存中。 - Ctx
@Ctx 无论CPU理解的字节序是什么,这都与网络无关。IP协议使用大端字节序,因此网络头中的所有信息都必须使用大端字节序,包括IP地址、端口、校验和等。这就是函数htons()及其相关函数存在的原因。如果您从socket.h库中读取任何套接字结构的32位IP,则无论体系结构如何,它都会立即以大端字节序出现。您需要使用ntohl()将其转换为主机字节序。 - Havenard
@Ctx 我明确说明这是一个随机示例,而不是特定的IP地址,我不关心它在哪个地址,我只是用垃圾初始化了变量。 - Havenard
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接