核心聯結器

核心聯結器 - 一個基於 netlink、易於使用的使用者空間 <-> 核心空間通訊模組。

聯結器驅動程式使得使用基於 netlink 的網路連線各種代理變得容易。必須註冊一個回撥函式和一個識別符號。當驅動程式收到帶有適當識別符號的特殊 netlink 訊息時,將呼叫相應的回撥函式。

從使用者空間的角度來看,這非常直接

  • socket();

  • bind();

  • send();

  • recv();

但是如果核心空間想要充分利用此類連線的功能,驅動程式編寫者必須建立特殊套接字,必須瞭解 struct sk_buff 的處理等... 聯結器驅動程式允許任何核心空間代理以顯著更簡單的方式使用基於 netlink 的網路進行程序間通訊

int cn_add_callback(const struct cb_id *id, char *name, void (*callback) (struct cn_msg *, struct netlink_skb_parms *));
void cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group, int gfp_mask);
void cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, int gfp_mask);

struct cb_id
{
      __u32                   idx;
      __u32                   val;
};

idx 和 val 是唯一的識別符號,必須在 connector.h 標頭檔案中註冊以供核心內部使用。void (*callback) (void *) 是一個回撥函式,當聯結器核心收到帶有上述 idx.val 的訊息時,將呼叫該函式。該函式的引數必須被解引用為 struct cn_msg *

struct cn_msg
{
      struct cb_id            id;

      __u32                   seq;
      __u32                   ack;

      __u16                   len;    /* Length of the following data */
      __u16                   flags;
      __u8                    data[0];
};

聯結器介面

int cn_add_callback(const struct cb_id *id, const char *name, void (*callback)(struct cn_msg*, struct netlink_skb_parms*))

向聯結器核心註冊新的回撥函式。

引數

const struct cb_id *id

唯一的聯結器使用者識別符號。它必須在 connector.h 中註冊,以供合法的核心內部使用者使用。

const char *name

聯結器回撥函式的符號名稱。

void (*callback)(struct cn_msg *, struct netlink_skb_parms *)

聯結器回撥函式。引數是 cn_msg 和傳送者的憑據

void cn_del_callback(const struct cb_id *id)

從聯結器核心登出回撥函式。

引數

const struct cb_id *id

唯一的聯結器使用者識別符號。

向指定的組傳送訊息。

引數

struct cn_msg *msg

訊息頭(附帶資料)。

u16 len

要傳送的 msg 數量。

u32 portid

目的埠。如果非零,訊息將傳送到給定埠,該埠應設定為原始傳送方。

u32 group

目的組。如果 portidgroup 為零,則將在所有已註冊的聯結器使用者中搜索適當的組,並將訊息傳遞給為與 msg 中 ID 相同的使用者建立的組。如果 group 不為零,則訊息將傳遞到指定的組。

gfp_t gfp_mask

GFP 掩碼。

netlink_filter_fn filter

在 netlink 層使用的過濾函式。

void *filter_data

提供給過濾函式的資料

描述

可以在軟中斷上下文安全呼叫,但在記憶體壓力大時可能會靜默失敗。

如果給定組沒有偵聽器,可能會返回 -ESRCH

向指定的組傳送訊息。

引數

struct cn_msg *msg

訊息頭(附帶資料)。

u32 portid

目的埠。如果非零,訊息將傳送到給定埠,該埠應設定為原始傳送方。

u32 group

目的組。如果 portidgroup 為零,則將在所有已註冊的聯結器使用者中搜索適當的組,並將訊息傳遞給為與 msg 中 ID 相同的使用者建立的組。如果 group 不為零,則訊息將傳遞到指定的組。

gfp_t gfp_mask

GFP 掩碼。

描述

可以在軟中斷上下文安全呼叫,但在記憶體壓力大時可能會靜默失敗。

如果給定組沒有偵聽器,可能會返回 -ESRCH

注意

註冊新的回撥使用者時,聯結器核心會將一個 netlink 組分配給該使用者,該組等於其 id.idx。

協議描述

當前框架提供了一個帶有固定頭部的傳輸層。使用此類頭部的推薦協議如下

msg->seq 和 msg->ack 用於確定訊息的源流。當有人傳送訊息時,他們使用本地唯一的序列號和隨機確認號。序列號也可以複製到 nlmsghdr->nlmsg_seq 中。

序列號隨每條傳送的訊息遞增。

如果您期望訊息有回覆,那麼接收到的訊息中的序列號必須與原始訊息中的序列號相同,並且確認號必須是原始序列號加 1。

如果我們收到的訊息的序列號與我們期望的不相等,那麼這是一條新訊息。如果我們收到的訊息的序列號與我們期望的相同,但其確認號不等於原始訊息中的序列號加 1,那麼這也是一條新訊息。

顯然,協議頭中包含上述 ID。

聯結器允許以下形式的事件通知:核心驅動程式或使用者空間程序可以要求聯結器在選定的 ID 開啟或關閉(註冊或登出其回撥)時通知它。這是透過向聯結器驅動程式傳送一個特殊命令來完成的(聯結器驅動程式也用 id={-1, -1} 註冊自己)。

這種用法的一個示例可以在 cn_test.c 模組中找到,該模組使用聯結器請求通知和傳送訊息。

可靠性

Netlink 本身不是一個可靠的協議。這意味著訊息可能會因記憶體壓力或程序接收佇列溢位而丟失,因此呼叫者被警告必須做好準備。這就是 struct cn_msg [聯結器主要訊息頭] 包含 u32 seq 和 u32 ack 欄位的原因。

使用者空間用法

2.6.14 版本有一個新的 netlink 套接字實現,預設情況下不允許向除組 1 以外的 netlink 組傳送資料。因此,如果您希望使用帶有不同組號的 netlink 套接字(例如使用聯結器),使用者空間應用程式必須首先訂閱該組。這可以透過以下虛擬碼實現

s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);

l_local.nl_family = AF_NETLINK;
l_local.nl_groups = 12345;
l_local.nl_pid = 0;

if (bind(s, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)) == -1) {
      perror("bind");
      close(s);
      return -1;
}

{
      int on = l_local.nl_groups;
      setsockopt(s, 270, 1, &on, sizeof(on));
}

其中上面的 270 是 SOL_NETLINK,1 是 NETLINK_ADD_MEMBERSHIP 套接字選項。要取消多播訂閱,應使用定義為 0 的 NETLINK_DROP_MEMBERSHIP 引數呼叫上述套接字選項。

2.6.14 版本的 netlink 程式碼只允許選擇小於或等於 netlink_kernel_create() 時使用的最大組號的組。對於聯結器,它是 CN_NETLINK_USERS + 0xf,因此如果您想使用組號 12345,您必須將 CN_NETLINK_USERS 增加到該數字。額外分配的 0xf 個數字供非核心使用者使用。

由於此限制,組 0xffffffff 現在無法工作,因此無法使用新增/刪除聯結器組的通知,但據我所知,只有 cn_test.c 測試模組使用了它。

netlink 領域的一些工作仍在進行中,因此在 2.6.15 時間段內可能會發生變化,如果發生,將為該核心更新文件。

程式碼示例

聯結器測試模組和使用者空間的程式碼示例可在 samples/connector/ 中找到。要構建此程式碼,請啟用 CONFIG_CONNECTOR 和 CONFIG_SAMPLES。