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

歡迎訪問 生活随笔!

生活随笔

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

linux

tcp/ip 协议栈Linux内核源码分析十 邻居子系统分析一 概述通用邻居框架

發布時間:2025/4/5 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tcp/ip 协议栈Linux内核源码分析十 邻居子系统分析一 概述通用邻居框架 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

內核版本:3.4.39

為什么需要鄰居子系統呢?因為在網絡上發送報文的時候除了需要知道目的IP地址還需要知道鄰居的L2 mac地址,為什么是鄰居的L2地址而不是目的地的L2地址呢,這是因為目的地網絡可能不在同一個網段甚至不在同一個地區,因此需要借助其它離目的地近的網點幫我們傳輸下,這里離目的地近的網點通常就是網關,也就是鄰居。如果目的地和我們在同一個LAN上的話,它們就是鄰居。鄰居子系統的核心功能就是完成L3地址到L2地址的映射,并提供網絡層和驅動程序底層之間的接口。通過下面這張圖可以看到鄰居子系統在Linux內核協議棧的位置。IPv4和IPv6屬于網絡層,當需要傳輸數據的時候會通過鄰居子系統提供的發送接口發送數據。

具體來說,當發送數據的時候,在鄰居表里面查找鄰居項,查找關鍵詞就是設備和目的地址,找到這個鄰居項之后,就調,用鄰居項提供的接口發送出去。那么問題來了,鄰居項是什么?鄰居項是如何分配的?鄰居項的組織結構又是什么樣子?此外,鄰居項的管理又該怎么做?以上這些問題就是鄰居子系統需要解決的問題。一步步來分析。

首先鄰居項是一個存儲了到達鄰居信息的結構體,如下:

struct neighbour {struct neighbour __rcu *next; //指向下一個鄰居項struct neigh_table *tbl; //鄰居表struct neigh_parms *parms; //鄰居協議參數unsigned long confirmed; //可到達性確認時間unsigned long updated; //鄰居狀態更新時間rwlock_t lock; //讀寫鎖atomic_t refcnt; //引用計數struct sk_buff_head arp_queue; //發送緩存隊列unsigned int arp_queue_len_bytes; //發送緩存隊列長度struct timer_list timer; //鄰居項定時器unsigned long used; //使用時間標志位atomic_t probes; //探測次數__u8 flags;__u8 nud_state; //鄰居狀態標志位__u8 type; //地址類型__u8 dead; //廢棄標志位seqlock_t ha_lock; //地址保護鎖unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];struct hh_cache hh; //L2幀頭緩存int (*output)(struct neighbour *, struct sk_buff *); //提供給L3的發送接口const struct neigh_ops *ops; //虛擬函數表,隨鄰居狀態變更struct rcu_head rcu;struct net_device *dev; //設備u8 primary_key[0]; //占位符,保存地址信息 };

結構體里面信息雖然比較多,但是每一個就是必要的。?鄰居子系統提供了一套通用的框架,供鄰居協議使用,目前使用的協議包括ARP(IPv4),NDIPv6)。雖然協議不同,但是都使用了同一套結構體。每個協議會建立自己的鄰居表(struct neigh_table),arp使用的是arp_tbl,ND協議使用nd_tabl,table結構體如下:

struct neigh_table {struct neigh_table *next; //指向下一個鄰居表int family; //協議,AF_INET, AF_INET6int entry_size; //鄰居項大小int key_len; //地址長度,IPv4是4字節,IPv6是16字節__u32 (*hash)(const void *pkey, //計算hash值的函數const struct net_device *dev,__u32 *hash_rnd);int (*constructor)(struct neighbour *); //鄰居項構造函數int (*pconstructor)(struct pneigh_entry *); //代理鄰居項的構造函數void (*pdestructor)(struct pneigh_entry *);void (*proxy_redo)(struct sk_buff *skb);char *id; //鄰居表IDstruct neigh_parms parms; //鄰居表配置參數/* HACK. gc_* should follow parms without a gap! */int gc_interval; //gc回收時間int gc_thresh1; //鄰居表占用內存閾值int gc_thresh2;int gc_thresh3;unsigned long last_flush; //記錄gc上一次清理時間struct delayed_work gc_work; //gc任務隊列struct timer_list proxy_timer; //代理功能定時器struct sk_buff_head proxy_queue; //代理隊列atomic_t entries; //鄰居項個數rwlock_t lock; //讀寫鎖unsigned long last_rand;struct neigh_statistics __percpu *stats; //統計信息struct neigh_hash_table __rcu *nht; //鄰居項hash表struct pneigh_entry **phash_buckets; //代理鄰居項表 };

?鄰居表的元素nht是一個hash鏈表,所有相同協議的鄰居項都掛在這里。鄰居表和鄰居表項組織圖如下:

?上述就是鄰居表項的組織結構。

當網絡層發送報文前首先需要查找路由,出口路由是和鄰居綁定的。路由查找完成后會調用鄰居層提供的output接口發送。發送函數output會隨著鄰居項的狀態改變。鄰居項的狀態?當然啦,鄰居項也是有狀態的,比如說剛建立鄰居項的時候,這時候還不知道鄰居的MAC地址,這個鄰居項還不能使用,因為是初始化,所以狀態時NONE,這個時候如果發送報文的話時沒辦法發送出去的,必須先使用鄰居協議發送solicit請求,這個時候鄰居項的狀態就會變成INCOMPLETE(未完成),創建鄰居項的時候會自動起一個定時器,當定時器超時的時候會檢查當前鄰居項的狀態并作出適當改變。當發送solict請求一段時間沒有響應回來的話定時器就會超時,這時候會根據當前狀態判斷是否需要重傳,重傳的次數一定的,不可能一直重傳下去,每次重傳后定時器會自動重啟,定時器超時的時間也是根據配置來的,重傳的定時器時間是neigh->parms->retrans_time。此外,在發送solicti請求期間是沒法傳輸報文的,這個時候怎么辦呢,總不能系統就停在這里吧,當然也不能丟棄報文,可能鄰居一會兒就響應了。這個時候需要把這個報文放到neigh->arp_queue緩存隊列里,當然隊列是有長度的,不可能無線存儲,不然內存就不夠了,默認是存儲三個報文,溢出后簡單丟棄最先進來的。隊列長度是可配的。

假設收到了響應,這時候鄰居狀態就會從INCOMPLETE狀態遷移到REACHABLE(可到達),這個時候鄰居是可到達的,除了遷移狀態外還需要把緩存隊列里面的報文發送出去。

當然狀態不可能一直是Reachable(可到達),可能鄰居down掉了,或者我們設備自己掛掉了,這個時候鄰居狀態必須更改。通常情況下,如果一段時間不用,鄰居狀態就會從reachable狀態遷移到stale(舊)狀態,這個時候需要可到達性確認了。

如果在gc_staletime沒有使用的話狀態就會遷移到fail,此時gc定時回收。如果在gc_staletime有使用的話,狀態遷移到delay狀態,相當于延遲遷移到fail狀態,在delay狀態經過delay_probe_time狀態沒有更新的話就會進入probe狀態,這個狀態下需要主動發送探測報文,發送探測報文的次數是有限的,超時的話就只能丟棄了。

鄰居狀態遷移圖如下:

鄰居子系統的內容基本上就是這些,提供的主要是抽象的公共框架,不同的鄰居協議可以直接拿來使用,它的啟動流程比起內核其它模塊來說是簡單的不能再簡單了。

//鄰居子系統初始化 static int __init neigh_init(void) {//注冊應用層回調處理函數,用于處理鄰居添加、刪除、查詢等操作rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL);rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL);rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL);rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info,NULL);rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL);return 0; }

?僅僅是注冊應用層的回調處理函數,比如下面這條,添加一條鄰居項,通過dev設備到達10.0.0.3需要發送到0:0:0:0:0:1

ip neigh add 10.0.0.3 lladdr 0:0:0:0:0:1 dev eth0 nud perm

這條命令會通過netlink下發到內核,最終由鄰居子系統注冊的neigh_add函數處理,這個函數首先進行參數的合理性檢查,沒問題的話就將其加入到對應的鄰居表中。

//添加鄰居 static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) {struct net *net = sock_net(skb->sk);struct ndmsg *ndm;struct nlattr *tb[NDA_MAX+1];struct neigh_table *tbl;struct net_device *dev = NULL;int err;ASSERT_RTNL();//參數合法性檢查err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);if (err < 0)goto out;err = -EINVAL;//鄰居目的地址都不存在的 話就不要繼續搞了//畢竟鄰居項的靈魂之一就是L3地址if (tb[NDA_DST] == NULL)goto out;ndm = nlmsg_data(nlh);if (ndm->ndm_ifindex) {//提取出口設備,如果獲取失敗的話就返回錯誤,鄰居項是和出口綁定的dev = __dev_get_by_index(net, ndm->ndm_ifindex);if (dev == NULL) {err = -ENODEV;goto out;}//檢查鄰居L2地址長度是否合法if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len)goto out;}read_lock(&neigh_tbl_lock);//遍歷鄰居表,可能的選項包括IPv4的arp_tbl和IPv6的nd_tblfor (tbl = neigh_tables; tbl; tbl = tbl->next) {//標志位表示admin用戶權限和覆蓋選項int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;struct neighbour *neigh;void *dst, *lladdr;//協議要匹配,可能的值包括AF_INET和AF_INET6if (tbl->family != ndm->ndm_family)continue;read_unlock(&neigh_tbl_lock);//檢查長度是否合法,IPv4長度為4,IPv6長度為16if (nla_len(tb[NDA_DST]) < tbl->key_len)goto out;dst = nla_data(tb[NDA_DST]);lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;//添加代理if (ndm->ndm_flags & NTF_PROXY) {struct pneigh_entry *pn;err = -ENOBUFS;//查找代理表,如果存在的話則更新,不存在則新建pn = pneigh_lookup(tbl, net, dst, dev, 1);if (pn) {pn->flags = ndm->ndm_flags;err = 0;}goto out;}if (dev == NULL)goto out;//先查找鄰居表項是否存在neigh = neigh_lookup(tbl, dst, dev);if (neigh == NULL) {//鄰居不存在,如果沒有創建標志位報錯返回if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {err = -ENOENT;goto out;}//和neigh_lookup類似,不過它在查找失敗的話會自動創建新的鄰居項neigh = __neigh_lookup_errno(tbl, dst, dev);if (IS_ERR(neigh)) {err = PTR_ERR(neigh);goto out;}} else {//如果存在排他標志位的話,返回已經在錯誤if (nlh->nlmsg_flags & NLM_F_EXCL) {err = -EEXIST;neigh_release(neigh);goto out;}//如果不存在替換標志位的話,就不要覆蓋了if (!(nlh->nlmsg_flags & NLM_F_REPLACE))flags &= ~NEIGH_UPDATE_F_OVERRIDE;}if (ndm->ndm_flags & NTF_USE) {//發送探測報文,這個標志具體含義我還沒搞清楚,可能是立即使用吧//這時候立即調用neigh_event_send發送solicit請求進行可到達性確認neigh_event_send(neigh, NULL);err = 0;} else//更新鄰居表項err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);//釋放引用計數,查找的時候會加一個,這時候不用了減去 neigh_release(neigh);goto out;}read_unlock(&neigh_tbl_lock);err = -EAFNOSUPPORT; out:return err; }

?

參考文檔:

1. 《深入理解Linux網絡技術內幕》

總結

以上是生活随笔為你收集整理的tcp/ip 协议栈Linux内核源码分析十 邻居子系统分析一 概述通用邻居框架的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 97成人人妻一区二区三区 | 闫嫩的18sex少妇hd | 中日韩男男gay无套 人人草人人干 | 欧美人与禽zozzo性之恋的特点 | 99精品久久久久久中文字幕 | 草久久久久 | 国产一区精品无码 | 国精无码欧精品亚洲一区蜜桃 | 国产网红主播精品av | 欧美激情图片 | 精品无码久久久久 | 一级日韩一级欧美 | 成人77777 | 国产一区二区三区在线观看 | 欧美日韩91 | 人妻在卧室被老板疯狂进入 | www.污视频 | 免费av在线播放 | 日本一区二区三区精品 | 伊人影院网| 日韩在线观看视频一区二区三区 | 亚洲熟妇一区二区 | 手机av在线网 | 91视频在线免费看 | 亚洲www在线 | 性色av蜜臀av浪潮av老女人 | 国产午夜电影在线观看 | 九九热精品视频在线 | 久久精品国产一区二区 | 女生张开腿给男生桶 | 国产丝袜美腿一区二区三区 | 日韩免费av在线 | xxxxx色 | 欧美黑大粗 | 美女视频在线免费观看 | 天天躁日日躁bbbbb | 成人三级电影网站 | 色一情| 精品国产一区二区三区久久狼黑人 | 99久久亚洲精品日本无码 | 国产日产亚洲精品 | 中文字幕无人区二 | 91操视频| 97视频久久久 | 成人国产精品 | 就是喜欢被他干 | 亚洲老老头同性老头交j | 欧美人妻一区二区 | 欧美成人乱码一区二区三区 | 九九久久精品视频 | 中文永久免费观看 | 国产丝袜av| 亚洲精品一区二区三区婷婷月 | 99久久久无码国产精品 | 九色.com | 亚洲第一区在线播放 | 日本美女三级 | 91黄色国产 | 九九九久久久久 | 国产三级全黄 | 在线看片黄 | 国内爆初菊对白视频 | 国产精品久久久久久久专区 | 国产全肉乱妇杂乱视频 | 大尺度做爰呻吟舌吻网站 | 欧美人体视频一区二区三区 | 国产男男一区二区三区 | 国产美女福利在线 | 8x8ⅹ国产精品一区二区二区 | 日本美女黄网站 | 国产成年人 | 亚洲二区在线观看 | 免费在线激情视频 | 青娱乐极品视频在线 | 亚洲一级片在线播放 | 亚洲精品视频中文字幕 | 禁果av一区二区三区 | 狠狠操夜夜操 | 男人狂揉女人下部视频 | 成年人晚上看的视频 | av男人的天堂在线观看 | 成人免费黄色大片 | 日韩欧美电影一区二区三区 | 亚洲欧美日韩久久 | 黄网站在线观看视频 | 私密spa按摩按到高潮 | 精品国产乱码久久久久久影片 | 国产av日韩一区二区三区精品 | 久久av高潮av无码av喷吹 | 五月天黄色网址 | 天天爽天天做 | 特黄做受又粗又大又硬老头 | 尤物在线精品 | 天天操天天插天天射 | 综合色99 | 少妇又白又嫩又色又粗 | 日韩女女同性aa女同 | 日本手机看片 | 红桃视频91 |