日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表

發(fā)布時間:2023/12/9 linux 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

From: http://yangelc.blog.sohu.com/68245920.html


Linux 用戶態(tài)與內(nèi)核態(tài)的交互

? 在 Linux 2.4 版以后版本的內(nèi)核中,幾乎全部的中斷過程與用戶態(tài)進程的通信都是使用 netlink 套接字實現(xiàn)的,例如iprote2網(wǎng)絡(luò)管理工具,它與內(nèi)核的交互就全部使用了netlink,著名的內(nèi)核包過濾框架Netfilter在與用戶空間的通讀,也在最新版本中改變?yōu)閚etlink,無疑,它將是Linux用戶態(tài)與內(nèi)核態(tài)交流的主要方法之一。它的通信依據(jù)是一個對應于進程的標識,一般定為該進程的 ID。當通信的一端處于中斷過程時,該標識為 0。當使用 netlink 套接字進行通信,通信的雙方都是用戶態(tài)進程,則使用方法類似于消息隊列。但通信雙方有一端是中斷過程,使用方法則不同。netlink 套接字的最大特點是對中斷過程的支持,它在內(nèi)核空間接收用戶空間數(shù)據(jù)時不再需要用戶自行啟動一個內(nèi)核線程,而是通過另一個軟中斷調(diào)用用戶事先指定的接收函數(shù)。 《UNIX Network Programming Volume 1 - 3rd Edition》第18章 講到BSD UNIX系統(tǒng)中routing socket的應用,這種套接字是按下面方式生成的:
rt_socket = socket(AF_ROUTE, SOCK_RAW, 0);
然后就可以用它跟內(nèi)核交互,進行網(wǎng)絡(luò)環(huán)境管理的操作,如讀取/設(shè)置/刪除路由表信息,更改網(wǎng)關(guān)等等,但書中所列代碼只在4.3BSD及以后版本的原始UNIX系統(tǒng)下可用,Linux雖然實現(xiàn)了AF_ROUTE族套接字,但用法卻完全不同。由于網(wǎng)上這方面知識的資料想對匱乏,現(xiàn)對Linux下routing socket的使用做一介紹。
由于我現(xiàn)在在Magic Linux1.0下工作,所以以下的講解全部基于2.4.10內(nèi)核。Linux從v2.2開始引入這一機制,因此可以肯定從v2.2到v2.4的內(nèi)核都是適用的,更新的v2.6我沒有試過。

Linux下雖然也有AF_ROUTE族套接字,但是這個定義只是個別名,請看
/usr/include/linux/socket.h, line 145:
#define AF_ROUTE AF_NETLINK /* Alias to emulate 4.4BSD */
可見在Linux內(nèi)核當中真正實現(xiàn)routing socket的是AF_NETLINK族套接字。AF_NETLINK族套接字像一個連接用戶空間和內(nèi)核的雙工管道,通過它,用戶進程可以修改內(nèi)核運行參數(shù)、讀取和設(shè)置路由信息、控制特定網(wǎng)卡的up/down狀態(tài)等等,可以說是一個管理網(wǎng)絡(luò)資源的絕佳途徑。

1 生成所需套接字,并綁定一個sockaddr結(jié)構(gòu)

先來看如何生成一個AF_NETLINK族套接字:
sockfd = socket(AF_NETLINK, socket_type, netlink_faimly);
這里socket_type可選SOCK_DGRAM或SOCK_RAW;因為AF_NETLINK族是面向數(shù)據(jù)報的套接字,所以不能使用SOCK_STREAM。
netlink_family指定要和內(nèi)核中的哪個子系統(tǒng)進行交互,目前支持:
NETLINK_ROUTE 與路由信息相關(guān),包括查詢、設(shè)置和刪除路由表中的條目等。待會兒我們將以這類family舉個實際的例子;
NETLINK_FIREWALL 接收由IPv4防火墻代碼發(fā)送的包;
NETLINK_ARPD 可以在用戶空間進行arp緩存的管理;
NETLINK_ROUTE6 在用戶空間發(fā)送和接收路由表信息更新;
還有幾種雖然沒有實現(xiàn),但已經(jīng)有了定義,為以后擴展做好了準備。

接下來要給該套接字綁定一個sockaddr結(jié)構(gòu),實際上是一個sockaddr_nl結(jié)構(gòu)
struct sockaddr_nl {
sa_family_t nl_family; /*AF_NETLINK*/
unsigned short nl_pad; /* 0 */
pid_t nl_pid; /* 進程pid */
u_32 nl_groups; /* 多播組掩碼*/
}nl;
這個結(jié)構(gòu)一般按照注釋填好就可以了,nl_groups我也不知道怎么用,一般填零了,表示沒有多播。綁定:
bind(sockfd, (struct sockaddr*) &nl, sizeof(nl));

2 填充所需數(shù)據(jù)結(jié)構(gòu),并通過sendmsg()/send()等函數(shù)寫到套接字里去

到此為止,與內(nèi)核通信的準備工作就完成了,下面要做的工作是,選取適當?shù)臄?shù)據(jù)結(jié)構(gòu)進行填充,并作為sendmsg()的參數(shù)發(fā)送出去,并recv()收到的消息。這個數(shù)據(jù)結(jié)構(gòu)就是nlmsghdr,它只是一個信息頭,后面可以接任意長的數(shù)據(jù),這些數(shù)據(jù)實際上又是針對某一需求所采用的特定數(shù)據(jù)結(jié)構(gòu)。先來看nlmsghdr:
struct nlmsghdr {
_u32 nlmsg_len; /* Length of msg including header */
_u32 nlmsg_type; /* 操作命令 */
_u16 nlmsg_flags; /* various flags */
_u32 nlmsg_seq; /* Sequence number */
_u32 nlmsg_pid; /* 進程PID */
};
/* 緊跟著是實際要發(fā)送的數(shù)據(jù),長度可以任意 */

nlmsg_type決定這次要執(zhí)行的操作,如查詢當前路由表信息,所使用的就是RTM_GETROUTE。標準nlmsg_type包括:NLMSG_NOOP, NLMSG_DONE, NLMSG_ERROR等。根據(jù)采用的nlmsg_type不同,還要選取不同的數(shù)據(jù)結(jié)構(gòu)來填充到nlmsghdr后面:
操作 數(shù)據(jù)結(jié)構(gòu)
RTM_NEWLINK ifinfomsg
RTM_DELLINK
RTM_GETLINK
RTM_NEWADDR ifaddrmsg
RTM_DELADDR
RTM_GETADDR
RTM_NEWROUTE rtmsg
RTM_DELROUTE
RTM_GETROUTE
RTM_NEWNEIGH ndmsg/nda_chcheinfo
RTM_DELNEIGH
RTM_GETNEIGH
RTM_NEWRULE rtmsg
RTM_DELRULE
RTM_GETRULE
RTM_NEWQDISC tcmsg
RTM_DELQDISC
RTM_GETQDISC
RTM_NEWTCLASS tcmsg
RTM_DELTCLASS
RTM_GETTCLASS
RTM_NEWTFILTER tcmsg
RTM_DELTFILTER
RTM_GETTFILTER
由于情形眾多,我從現(xiàn)在開始將用一個特定的例子來說明問題。我們的目的是從內(nèi)核讀取IPV4路由表信息。從上面表看,nlmsg_type一定使用RTM_xxxROUTE操作,對應的數(shù)據(jù)結(jié)構(gòu)是rtmsg。既然是讀取,那么應該是RTM_GETROUTE了。
struct rtmsg {
unsigned char rtm_family; /* 路由表地址族 */
unsigned char rtm_dst_len; /* 目的長度 */
unsigned char rtm_src_len; /* 源長度 */ (2.4.10頭文件的注釋標反了?)
unsigned char rtm_tos; /* TOS */

unsigned char rtm_table; /* 路由表選取 */
unsigned char rtm_protocol; /* 路由協(xié)議 */
unsigned char rtm_scope;
unsigned char rtm_type;

unsigned int rtm_flags;
};
對于RTM_GETROUTE操作來說,我們只需指定兩個成員:rtm_family:AF_INET, rtm_table: RT_TABLE_MAIN。其他成員都初始化為0即可。將這個結(jié)構(gòu)體跟nlmsghdr結(jié)合起來,得到我們自己的新結(jié)構(gòu)體:
struct {
struct nlmsghdr nl;
struct rtmsg rt;
}req;
填充好rt結(jié)構(gòu)之后,還要調(diào)整nl結(jié)構(gòu)相應成員的值。Linux定義了多個宏來處理nlmsghdr成員的值,我們這里用到的是NLMSG_LENGTH(size_t len);
req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
這將計算nlmsghdr長度與rtmsg長度的和(其中包括了將rtmsg進行4字節(jié)邊界對齊的調(diào)整),并存儲到nlmsghdr的nlmsg_len成員中。接下來要做的就是將這個新結(jié)構(gòu)體req放到sendmsg()函數(shù)的msghdr.iov處,并調(diào)用函數(shù)。
sendmsg(sockfd, &msg, 0);

3 接收數(shù)據(jù),并進行分析

接下來的操作是recv()操作,從該套接字讀取內(nèi)核返回的數(shù)據(jù),并進行分析處理。
recv(sockfd, p, sizeof(buf) - nll, 0);
其中p是指向一個緩沖區(qū)buf的指針,nll是已接收到的nlmsghdr數(shù)據(jù)的長度。
由于內(nèi)核返回信息是一個字節(jié)流,需要調(diào)用者檢查消息結(jié)尾。這是通過檢查返回的nlmsghdr的nlmsg_type是否等于NLMSG_DONE來完成的。返回的數(shù)據(jù)格式如下:
-----------------------------------------------------------
| nlmsghdr+route entry | nlmsghdr+route entry | .........
-----------------------------------------------------------
| 解出route entry
V
-----------------------------------------------------------
| dst_addr | gateway | Output interface| ...............
-----------------------------------------------------------
可以看出,返回消息由多個(nlmsghdr + route entry)組成,當某個nlmsghdr的nlmsg_type == NLMSG_DONE時就表示信息輸出已經(jīng)完畢。而每一個route entry由多個rtattr結(jié)構(gòu)體組成,每個結(jié)構(gòu)體表示該路由項的某個屬性,如目的地址,網(wǎng)關(guān)等等。根據(jù)這個示意圖我們就能夠輕松解析需要的數(shù)據(jù)了。

總結(jié)

以上是生活随笔為你收集整理的struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。