BPF_PROG_TYPE_CGROUP_SOCKOPT¶
BPF_PROG_TYPE_CGROUP_SOCKOPT 程式型別可以附加到兩個 cgroup 鉤子:
BPF_CGROUP_GETSOCKOPT- 每當程序執行getsockopt系統呼叫時呼叫。BPF_CGROUP_SETSOCKOPT- 每當程序執行setsockopt系統呼叫時呼叫。
上下文 (struct bpf_sockopt) 關聯著一個套接字 (sk) 和所有輸入引數:level, optname, optval 和 optlen。
BPF_CGROUP_SETSOCKOPT¶
BPF_CGROUP_SETSOCKOPT 在核心處理 sockopt 之前觸發,並且它具有可寫上下文:它可以在將引數傳遞給核心之前修改它們。這個鉤子可以訪問 cgroup 和套接字區域性儲存。
如果 BPF 程式將 optlen 設定為 -1,則在 cgroup 鏈中所有其他 BPF 程式完成之後,控制權將返回給使用者空間(即核心 setsockopt 處理將不會執行)。
請注意,optlen 不能增加超過使用者提供的值。它只能減小或設定為 -1。任何其他值都將觸發 EFAULT。
返回型別¶
0- 拒絕系統呼叫,EPERM將返回給使用者空間。1- 成功,繼續 cgroup 鏈中的下一個 BPF 程式。
BPF_CGROUP_GETSOCKOPT¶
BPF_CGROUP_GETSOCKOPT 在核心處理 sockopt 之後觸發。BPF 鉤子可以觀察 optval、optlen 和 retval,如果它對核心返回的任何值感興趣。BPF 鉤子可以覆蓋上述值,調整 optlen 並將 retval 重置為 0。如果 optlen 增加到超過初始 getsockopt 值(即使用者空間緩衝區太小),則返回 EFAULT。
此鉤子可以訪問 cgroup 和套接字區域性儲存。
請注意,retval 唯一可接受的設定值是 0 和核心返回的原始值。任何其他值都將觸發 EFAULT。
返回型別¶
0- 拒絕系統呼叫,EPERM將返回給使用者空間。1- 成功:將optval和optlen複製到使用者空間,從系統呼叫返回retval(請注意,這可能被父 cgroup 中的 BPF 程式覆蓋)。
Cgroup 繼承¶
假設存在以下 cgroup 層次結構,其中每個 cgroup 都以 BPF_F_ALLOW_MULTI 標誌在每個級別附加了 BPF_CGROUP_GETSOCKOPT。
A (root, parent)
\
B (child)
當應用程式從 cgroup B 呼叫 getsockopt 系統呼叫時,程式從下到上執行:B,A。第一個程式 (B) 看到核心 getsockopt 的結果。它可以選擇性地調整 optval, optlen 並將 retval 重置為 0。之後,控制權將傳遞給第二個程式 (A),A 將看到與 B 相同的上下文,包括任何潛在的修改。
BPF_CGROUP_SETSOCKOPT 也是如此:如果程式附加到 A 和 B,觸發順序是 B,然後是 A。如果 B 對輸入引數(level, optname, optval, optlen)進行任何更改,則鏈中的下一個程式 (A) 將看到這些更改,而不是原始的 setsockopt 輸入引數。潛在修改後的值將被傳遞給核心。
大型 optval¶
當 optval 大於 PAGE_SIZE 時,BPF 程式只能訪問該資料的前 PAGE_SIZE。因此它有兩種選擇:
將
optlen設定為零,這表示核心應使用使用者空間中的原始緩衝區。BPF 程式對optval所做的任何修改都將被忽略。將
optlen設定為小於PAGE_SIZE的值,這表示核心應使用 BPF 裁剪後的optval。
當 BPF 程式返回的 optlen 大於 PAGE_SIZE 時,使用者空間將接收原始的核心緩衝區,而不會包含 BPF 程式可能應用的任何修改。
示例¶
處理 BPF 程式的推薦方法如下:
SEC("cgroup/getsockopt")
int getsockopt(struct bpf_sockopt *ctx)
{
/* Custom socket option. */
if (ctx->level == MY_SOL && ctx->optname == MY_OPTNAME) {
ctx->retval = 0;
optval[0] = ...;
ctx->optlen = 1;
return 1;
}
/* Modify kernel's socket option. */
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
ctx->retval = 0;
optval[0] = ...;
ctx->optlen = 1;
return 1;
}
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > PAGE_SIZE)
ctx->optlen = 0;
return 1;
}
SEC("cgroup/setsockopt")
int setsockopt(struct bpf_sockopt *ctx)
{
/* Custom socket option. */
if (ctx->level == MY_SOL && ctx->optname == MY_OPTNAME) {
/* do something */
ctx->optlen = -1;
return 1;
}
/* Modify kernel's socket option. */
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
optval[0] = ...;
return 1;
}
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > PAGE_SIZE)
ctx->optlen = 0;
return 1;
}
有關處理套接字選項的 BPF 程式的示例,請參閱 tools/testing/selftests/bpf/progs/sockopt_sk.c。