netlink 是 Go 和核心模組之間優秀的通訊兵
netlink 是 Go 和核心模組之間優秀的通訊兵
netlink 是 Linux 系統裡使用者態程式、核心模組之間的一種 IPC 方式,特別是使用者態程式和核心模組之間的 IPC 通訊。比如在 Linux 終端裡常用的 ip 命令,就是使用 netlink 去跟核心進行通訊的。
TL,DR 使用 Go 通過 netlink 向核心模組傳送訊息,核心模組響應一條訊息,原始碼:核心模組,Go 程式。
核心模組
註冊 netlink
在 insmod custom-netlink.ko
載入核心模組時,註冊自定義的 netlink 處理函式。在 rmmod custom_netlink
解除安裝核心模組時,則登出自定義的 netlink 處理函式。
#define NETLINK_CUSTOM 31
static int __init custom_nl_init(void)
{
//This is for 3.6 kernels and above.
struct netlink_kernel_cfg cfg = {
.input = custom_nl_recv_msg,
};
nl_sk = netlink_kernel_create(&init_net, NETLINK_CUSTOM, &cfg);
......
return 0;
}
static void __exit custom_nl_exit(void)
{
netlink_kernel_release(nl_sk);
printk(KERN_INFO "[-] unregistered custom_netlink module.\n");
}
module_init(custom_nl_init);
module_exit(custom_nl_exit);
接收 netlink 訊息
訊息格式如下:
0 4 8
+-+-+-+-+-+-+-+-+
|msg len| data |
+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+
前 4 個位元組代表後面的資料長度,後跟著一段字串。為了防止在列印字串的時候記憶體越界了,將字串的最後一個字元置為 \0
。
列印字串後,將該字元響應回去。
enum
{
nlresp_result_unspec,
nlresp_result_ok,
nlresp_result_invalid
};
static void custom_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
unsigned char *nl_data;
unsigned char *msg;
__u32 msg_size;
nlh = nlmsg_hdr(skb);
nl_data = (unsigned char *)NLMSG_DATA(nlh);
msg_size = *(__u32*)nl_data;
if (msg_size > 1024) {
custom_nl_send_msg(nlh, nlresp_result_invalid, NULL, 0);
return;
}
msg = nl_data+4;
msg[msg_size-1] = '\0';
printk(KERN_INFO "[Y] [custom netlink] receive msg from user: %s\n", msg);
custom_nl_send_msg(nlh, nlresp_result_ok, msg, msg_size);
}
響應 netlink 訊息
typedef struct
{
__u32 result;
unsigned char data[0];
} custom_nl_resp_data_t;
static int custom_nl_send_msg(struct nlmsghdr *nlh, __u32 result, const unsigned char *data, __u32 data_size)
{
custom_nl_resp_data_t *resp;
struct nlmsghdr *nlh_resp;
struct sk_buff *skb;
int pid = nlh->nlmsg_pid, res = -1;
const unsigned char *resp_msg = "Echo from kernel: ";
data_size += 4+18; // 4 是 result 的長度,18 是 resp_msg 的長度
resp = kzalloc(data_size + 18, GFP_KERNEL);
memcpy(resp->data, resp_msg, 18); // 複製 resp_msg
memcpy(resp->data + 18, data, data_size-4); // 複製 Go 程式傳送過來的字串
resp->result = result;
skb = nlmsg_new(data_size, GFP_KERNEL); // 核心自動釋放記憶體,不需要手動釋放
if (!skb)
goto out;
nlh_resp = nlmsg_put(skb, pid, nlh->nlmsg_seq, NLMSG_DONE, data_size, 0);
memcpy(NLMSG_DATA(nlh_resp), resp, data_size); // 填充 netlink 訊息內容
res = nlmsg_unicast(nl_sk, skb, pid); // 將訊息響應給指定 pid 的程式
out:
kfree(resp); // 用後釋放記憶體
return res;
}
Go 程式
發起 netlink 連線
使用跟核心模組一致的 netlink family 發起連線。
const (
netlinkCustom = 31
)
conn, err := netlink.Dial(netlinkCustom, nil)
傳送 netlink 訊息
封裝 netlink 訊息,並將提供的字串複製到訊息中。
// msg 是前面提供的字串
data := make([]byte, 4+len(msg)+1)
nlenc.PutUint32(data[:4], uint32(len(msg)+1))
copy(data[4:], msg)
fmt.Println("Send to kernel:", msg)
var nlmsg netlink.Message
nlmsg.Data = data
msgs, err := conn.Execute(nlmsg)
接收 netlink 訊息
按如下格式接收訊息:
0 4 8
+-+-+-+-+-+-+-+-+
| result| data |
+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+
前 4 個位元組是核心模組響應的結果,後跟著一段字串。
msgs, err := conn.Execute(nlmsg)
......
nlmsg = msgs[0]
......
fmt.Println(string(nlmsg.Data[4:]))
實驗效果
➜ kernel-module-fun make
make -C /lib/modules/4.19.0/build M=/root/Projects/kernel-module-fun modules
make[1]: Entering directory '/usr/src/linux-headers-4.19.0'
CC [M] /root/Projects/kernel-module-fun/custom-netlink.o
Building modules, stage 2.
MODPOST 4 modules
CC /root/Projects/kernel-module-fun/custom-netlink.mod.o
LD [M] /root/Projects/kernel-module-fun/custom-netlink.ko
make[1]: Leaving directory '/usr/src/linux-headers-4.19.0'
➜ kernel-module-fun insmod custom-netlink.ko
➜ kernel-module-fun ./custom-netlink/custom-netlink
Send to kernel: Hello from Go
Echo from kernel: Hello from Go
➜ kernel-module-fun rmmod custom_netlink
➜ kernel-module-fun dmesg
[2424642.636765] [+] registered custom_netlink module!
[2424644.929387] [Y] [custom netlink] receive msg from user: Hello from Go
[2424647.822184] [-] unregistered custom_netlink module.
小結
使用 netlink 進行使用者態程式和核心模組的 IPC 通訊,能夠動態地變更核心模組的配置,甚至動態地去開啟、關閉核心模組裡的功能。比如在收到配置後,再掛載 netfilter hook。
使用 netlink 動態傳配置的功能,可以取代 insmod
傳參這一笨拙的傳配置方式。
P.S. go-iproute:使用 Go 基於 netlink 實現的 iproute2 工具庫
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 在Go和Python之間通過ActiveMQ通訊GoPythonMQ
- Tcp, WebSocket 和 http 之間的通訊TCPWebHTTP
- C++和pascal之間的通訊C++
- Java 和 Python 之間的 Socket 通訊JavaPython
- kubernetes實踐之十八:叢集各模組之間的通訊
- liunx使用者空間和核心空間之間的通訊實現(在PPC下的實現)(轉)
- 通訊兵(Chain of Responsibility) (轉)AI
- vue元件之間的通訊Vue元件
- Activity之間的通訊方式
- React - 元件之間的通訊React元件
- iOS架構設計解耦的嘗試之模組間通訊iOS架構解耦
- (八)Flutter 和 Native 之間的通訊詳解Flutter
- 父子元件之間通訊元件
- 田永強:優秀的JavaScript模組是怎樣煉成的JavaScript
- 元件之間的通訊LiveDataBus元件LiveData
- Flutter多Engine之間的通訊Flutter
- 程式和執行緒有什麼區別?(Process and Threads)程式之間和執行緒之間是如何通訊的?執行緒thread
- React之元件(component)之間的通訊React元件
- Javascript與Python之間的程序間通訊JavaScriptPython
- 微服務之間通過RabbitMQ通訊微服務MQ
- 解決go-micro與其它gRPC框架之間的通訊問題GoRPC框架
- 程序間的通訊(訊號通訊)
- Android 程式之間通訊Android
- webRtc及元件之間通訊Web元件
- iOS app之間通訊方式iOSAPP
- Flutter與android之間的通訊FlutterAndroid
- 實現不同程式之間的通訊
- JUC之執行緒間的通訊執行緒
- React中元件之間通訊的方式React元件
- 多執行緒之間的通訊執行緒
- Flink內部之間的通訊
- 網路核心之TCP是如何傳送和接收訊息的TCP
- 伺服器和伺服器之間通訊,我該用rmi還是socket?伺服器
- 什麼是程式間通訊?Linux程式間通訊有幾種方式?Linux
- 程式間通訊是什麼?Linux程式間通訊有幾種方式?Linux
- Nodejs核心模組之net和httpNodeJSHTTP
- js訊息訂閱和釋出實現元件之間通訊JS元件
- Vue元件之間通訊的三種方式Vue元件