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

歡迎訪問 生活随笔!

生活随笔

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

linux

tcp/ip 协议栈Linux内核源码分析九 IPv6分片ip6_fragment 分析

發布時間:2025/4/5 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tcp/ip 协议栈Linux内核源码分析九 IPv6分片ip6_fragment 分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

內核版本:3.4.39

IPv6的分片流程和IPv4基本一致,這一點內核源碼作者也說了。流程比較簡單,分片的時候判斷是否滿足快速分片,滿足的話直接一個接一個加上分片擴展選項發送出去,不滿足的話就只能走慢速分片通道了,這時候需要重新分配每一個skb,然后從原始SKB報文那里復制數據發送出去。

int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) {struct sk_buff *frag;struct rt6_info *rt = (struct rt6_info*)skb_dst(skb);struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;struct ipv6hdr *tmp_hdr;struct frag_hdr *fh;unsigned int mtu, hlen, left, len;int hroom, troom;__be32 frag_id = 0;int ptr, offset = 0, err=0;u8 *prevhdr, nexthdr = 0;struct net *net = dev_net(skb_dst(skb)->dev);//獲取不能分片的擴展選項長度,不是每個擴展選項都能夠分片的hlen = ip6_find_1stfragopt(skb, &prevhdr);nexthdr = *prevhdr;//獲取mtu大小mtu = ip6_skb_dst_mtu(skb);/* We must not fragment if the socket is set to force MTU discovery* or if the skb it not generated by a local socket.*///檢查下是否允許分片if (!skb->local_df && skb->len > mtu) {skb->dev = skb_dst(skb)->dev;icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGFAILS);kfree_skb(skb);return -EMSGSIZE;}//獲取mtu大小if (np && np->frag_size < mtu) {if (np->frag_size)mtu = np->frag_size;}//hlen表示每個分片報文都必須攜帶的擴展選項頭mtu -= hlen + sizeof(struct frag_hdr);//判斷是否存在分片隊列,存在的話說明應用層可以已經幫忙分片了,這時候只需要檢查//分片的長度是否合法,合法的話則可以使用快速分片。if (skb_has_frag_list(skb)) {int first_len = skb_pagelen(skb);struct sk_buff *frag2;//如果第一個報文長度大于MTU或者長度不是8字節的整數倍//克隆的報文也不能使用快速分片。if (first_len - hlen > mtu ||((first_len - hlen) & 7) ||skb_cloned(skb))goto slow_path;//檢查分片是否滿足快速分片要求skb_walk_frags(skb, frag) {/* Correct geometry. *///首先是長度不能大于MTU//此外除了最后一個報文其它報文長度必須是8字節整數倍//當然首部空間不夠的話也不行if (frag->len > mtu ||((frag->len & 7) && frag->next) ||skb_headroom(frag) < hlen)goto slow_path_clean;/* Partially cloned skb? */if (skb_shared(frag))goto slow_path_clean;BUG_ON(frag->sk);if (skb->sk) {frag->sk = skb->sk;frag->destructor = sock_wfree;}//報文要各自為戰了,所以從第一個報文那個分出來。//這么做是因為發送的時候需要將報文所占大小還給系統。//每個報文返還自己的。skb->truesize -= frag->truesize;}err = 0;offset = 0;//將分片從skb鏈表上掛載到frag上。frag = skb_shinfo(skb)->frag_list;skb_frag_list_init(skb);/* BUILD HEADER */*prevhdr = NEXTHDR_FRAGMENT;tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);if (!tmp_hdr) {IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGFAILS);return -ENOMEM;}//追加一個分片頭__skb_pull(skb, hlen);fh = (struct frag_hdr*)__skb_push(skb, sizeof(struct frag_hdr));__skb_push(skb, hlen);skb_reset_network_header(skb);memcpy(skb_network_header(skb), tmp_hdr, hlen);//選擇一個報文ID標識ipv6_select_ident(fh, rt);//設置分片擴展選項fh->nexthdr = nexthdr;fh->reserved = 0;fh->frag_off = htons(IP6_MF);frag_id = fh->identification;//重新調整長度first_len = skb_pagelen(skb);skb->data_len = first_len - skb_headlen(skb);skb->len = first_len;ipv6_hdr(skb)->payload_len = htons(first_len -sizeof(struct ipv6hdr));//增加路由統計計數dst_hold(&rt->dst);for (;;) {/* Prepare header of the next frame,* before previous one went down. *///處理其它分片報文 if (frag) {frag->ip_summed = CHECKSUM_NONE;skb_reset_transport_header(frag);//添加分片擴展選項fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr));__skb_push(frag, hlen);skb_reset_network_header(frag);memcpy(skb_network_header(frag), tmp_hdr,hlen);//調整選項頭 offset += skb->len - hlen - sizeof(struct frag_hdr);fh->nexthdr = nexthdr;fh->reserved = 0;fh->frag_off = htons(offset);if (frag->next != NULL)fh->frag_off |= htons(IP6_MF);fh->identification = frag_id;ipv6_hdr(frag)->payload_len =htons(frag->len -sizeof(struct ipv6hdr));ip6_copy_metadata(frag, skb);}//先將上一個報文發送出去//分片報文按照處理的順序發送出去err = output(skb);if(!err)IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),IPSTATS_MIB_FRAGCREATES);if (err || !frag)break;skb = frag;frag = skb->next;skb->next = NULL;}kfree(tmp_hdr);//分片成功則返回if (err == 0) {IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),IPSTATS_MIB_FRAGOKS);dst_release(&rt->dst);return 0;}//走到這里說明發送那里出現了錯誤,功虧一簣,剩下的報文原地解散回家去吧while (frag) {skb = frag->next;kfree_skb(frag);frag = skb;}//失敗的統計計數IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),IPSTATS_MIB_FRAGFAILS);//釋放占用的路由指針 dst_release(&rt->dst);return err;slow_path_clean:skb_walk_frags(skb, frag2) {if (frag2 == frag)break;frag2->sk = NULL;frag2->destructor = NULL;skb->truesize += frag2->truesize;}}slow_path://慢速通道,需要重新分配skb//left表示分片報文數據部分總長度left = skb->len - hlen; /* Space per frame */ptr = hlen; /* Where to start from *//** Fragment the datagram.*/*prevhdr = NEXTHDR_FRAGMENT;//預留鏈路層頭部長度hroom = LL_RESERVED_SPACE(rt->dst.dev);troom = rt->dst.dev->needed_tailroom;/** Keep copying data until we run out.*/while(left > 0) {len = left;/* IF: it doesn't fit, use 'mtu' - the data space left *///分片長度最大時mtuif (len > mtu)len = mtu;/* IF: we are not sending up to and including the packet endthen align the next start on an eight byte boundary *///除了最后一個分片報文,其它報文長度必須是8字節整數倍 if (len < left) {len &= ~7;}/** Allocate buffer.*///分配一個SKB,果然是慢速,快速已經分配好了直接發送就可以了。if ((frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +hroom + troom, GFP_ATOMIC)) == NULL) {NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n");IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGFAILS);err = -ENOMEM;goto fail;}/** Set up data on packet*///重新分配數據ip6_copy_metadata(frag, skb);skb_reserve(frag, hroom);skb_put(frag, len + hlen + sizeof(struct frag_hdr));skb_reset_network_header(frag);fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);frag->transport_header = (frag->network_header + hlen +sizeof(struct frag_hdr));/** Charge the memory for the fragment to any owner* it might possess*/if (skb->sk)skb_set_owner_w(frag, skb->sk);/** Copy the packet header into the new buffer.*/skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);/** Build fragment header.*///配置分片選項頭 fh->nexthdr = nexthdr;fh->reserved = 0;if (!frag_id) {ipv6_select_ident(fh, rt);frag_id = fh->identification;} elsefh->identification = frag_id;/** Copy a block of the IP datagram.*///復制其它數據 if (skb_copy_bits(skb, ptr, skb_transport_header(frag), len))BUG();left -= len;//設置分片選項頭fh->frag_off = htons(offset);if (left > 0)fh->frag_off |= htons(IP6_MF);ipv6_hdr(frag)->payload_len = htons(frag->len -sizeof(struct ipv6hdr));ptr += len;offset += len;/** Put this fragment into the sending queue.*///發送報文 err = output(frag);if (err)goto fail;IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGCREATES);}IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGOKS);kfree_skb(skb);return err;fail:IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),IPSTATS_MIB_FRAGFAILS);kfree_skb(skb);return err; }

?

總結

以上是生活随笔為你收集整理的tcp/ip 协议栈Linux内核源码分析九 IPv6分片ip6_fragment 分析的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 中文字幕乱码无码人妻系列蜜桃 | 大象传媒成人在线观看 | 亚洲AV无码久久精品国产一区 | 国产在线第一页 | av一级久久 | 国产又粗又猛视频 | 国产综合精品视频 | 蜜桃精品久久久久久久免费影院 | 国产精品久久久久野外 | 写真福利片hd在线播放 | 丰满岳妇伦在线播放 | 国产一级特黄 | 精品人妻一区二区三区久久 | 中文字幕第四页 | 天堂网2014av| av导航网站 | 超碰人人爱人人 | 亚洲高清网站 | 五月激情综合 | 国产精品伦理 | 欧美成人小视频 | 男生插女生的网站 | 国产一区二三区 | 免看黄大片aa| 日韩黄色片 | 久操视频精品 | 在线中文字日产幕 | 欧美日韩精品一区二区在线播放 | 一区二区三区在线免费 | 国产激情亚洲 | 污视频在线网站 | 日韩一中文字幕 | 国产人人草 | 欧美猛操 | 国产 中文 字幕 日韩 在线 | 成人免费午夜视频 | 黑帮大佬和我的三百六十五天 | 欧美激情一区二区三区在线 | 日韩精品人妻中文字幕 | 久人人| 欧美成视频 | 91精品视频免费看 | 饥渴放荡受np公车奶牛 | 亚洲无人区小视频 | 37p粉嫩大胆色噜噜噜 | 欧美性一区二区 | 国产专区欧美专区 | 国产四区| 成人看| 自拍偷拍亚洲综合 | 国产精品 欧美精品 | 精品成人免费视频 | 福利视频免费看 | 在线观看欧美一区二区 | 日韩精品自拍 | 丰满少妇在线观看bd | 日日夜夜欧美 | 亚洲品质自拍视频网站 | 在线观看你懂的视频 | 国产成人在线影院 | 97av在线| 依人成人网 | 国产精品成人国产乱一区 | 被扒开腿一边憋尿一边惩罚 | 在线天堂视频 | 天堂少妇| 一级欧美一级日韩 | 在线国产日韩 | 手机在线永久免费观看av片 | 欧美精品一区二区三区在线 | 郑艳丽三级 | h在线| 毛片啪啪啪 | 综合伊人av | 欧美成人午夜免费视在线看片 | 美女调教视频 | 国产精品综合在线 | 污漫在线观看 | 亚洲一区二区免费 | 亚洲一区二区三区加勒比 | 国产乱人乱精一区二视频国产精品 | 伊人色影院| 一区二区美女 | 日本黄大片在线观看 | 国产午夜精品免费一区二区三区视频 | 欧美在线中文字幕 | 久久久久久久影院 | 国产激情视频 | 夜夜干夜夜| 91精品国产一区二区三竹菊影视 | 欧美男女交配视频 | 你懂的网站在线观看 | 麻豆视频一区二区 | 国产女主播一区二区三区 | 亚洲日本成人在线观看 | 久免费一级suv好看的国产 | 国产欧美精品在线观看 | 精品孕妇一区二区三区 | 人妻熟人中文字幕一区二区 |