這個情人節,工程師用阿里雲來試著表達不一樣的愛意
年輕的時候談的戀愛就像TCP連結,戀愛時三次握手即可,可分手時卻分了四次。而常常久久的愛情,更像是icmp協議,無論對方身在何處,無論是否是可靠連線,無論你何時去ping她/他,她/他都默默地響應你。這篇文章就是說說,如何在核心中增加幾行程式碼,讓你的女神/男神當ping你(的伺服器)的時候,來傳達表達你的愛。效果如下(左邊為ping的結果,需要破解ascii碼轉換為對應字元,右邊為使用tcpdump抓包直接讀取的資訊):
對於UNIX_LIKE系統來說,如果ping的傳送內容與接收內容不同,會顯示不同的部分,那麼就讓你的女神或者男神,慢慢將ASCII碼解析成你想告訴她/他的話吧。或者告訴她/他,使用tcpdump來直接抓包隱藏在ping中的悄悄話。(對於windows來說本人沒有充分測試,只是知道不會像unix_like系統一樣直接顯示出請求訊息和回顯訊息的不同,所以需要大家抓包認真提取資訊)
一、ICMP協議這些你需要了解:
學過計算機網路的一定知道,一個網路包的封裝主要由多個屬於不同網路協議層的報文頭和使用者資料共同組成:鏈路層報文頭+網路層IP報文頭+傳輸層報文頭+攜帶的內容+幀尾。而ICMP報文在整個以太幀位於如下位置:
上圖顯示的是一個未分片ICMP報文或者是一個較長ICMP報文的第一個IP分片的報文(被分片的報文中不會帶有ICMP報頭)。RFC792(https://tools.ietf.org/html/rfc792)中定義了11種ICMP報文型別,通過ICMP報頭8bit”型別”欄位進行區分。並且每種”型別“會和其”程式碼”欄位以及報文頭的最後4位元組,共同表達每種報文型別所表示的資訊。這些ICMP報文型別被主要分為差錯報文和查詢報文:
- 查詢報文主要包括:回送請求(TYPE8),回送應答(TYPE0),地址掩碼或時間戳的請求/應答等
- 差錯報文主要包括:目標主機不可達(TYPE3),超時,源抑制,路由重定向等
ping作為ICMP協議最為典型的運用,主要和回送請求,和回送應答這兩個型別相關,這也是本文主要關心的兩個型別。當然,當主機不可達或者網路路由不可達出現的時候,ping會收到路由器傳來的TYPE為3的目標主機不可達的報文(我們可以通過tcpdump抓包獲取)。對於其他的型別,有興趣的同學可以自行學習,如icmp重定向攻擊,洪水攻擊都是利用了ICMP協議進行的網路攻擊。
二、動手寫一個簡單的ping,瞭解Linux ping
作為本文的主角之一ping,有必要動手寫一個簡單的ping,幫助我們更好的理解整個請求應答的過程。我本人的測試機器centos 7中使用的是iputils這個工具進行ping操作,所以我們可以從iputils原始碼入手學習如何寫一個簡單的ping。
學習過c網路程式設計的一定都瞭解socket套接字這個概念。對於ping來說傳送請求和接受應答也同樣是通過套接字來完成。只不過,ICMP協議雖然在核心中和TCP、UDP相似屬於L4層協議,但是本質是附屬於IP協議的網路層協議,所以需要使用原始套接字(SOCK_RAW)構建套接字,而非TCP或UDP使用的流式套接字(SOCK_STREAM)和資料包式套接字(SOCK_DGRAM)。SOCK_RAW的用途在於使用者可以自定義填充IP報文頭,並且對於ICMP報文自定義填充ICMP報文頭。下面一張圖,展示了程式碼中整個ping的邏輯傳送以及處理應答的邏輯。
具體程式碼可以參考這個:https://github.com/xiaobaidemu/myping/blob/master/ping.c 整個流程非常簡單,需要說明的是,對於ping 127.0.0.1來說,程式極有可能先收到type為0的回顯請求報文,再收到type為8的回顯應答報文。這是因為icmp報文可以同時被核心接收處理,也會被原始套接字接收處理,如下為Understanding Linux Network Internals書中所述。
三、新增核心程式碼前,你只需要知道一個結構體和icmp.c
理解了ping的整個過程,接下來就是需要修改核心來傳達你想說的話。但是最重要的是,需要分析出修改的位置,即回顯應答可能傳送的位元組在核心程式碼中的位置。這裡有一個非常重要的結構體——struct sk_buff,其定義位於<include/linux/skbuff.h>。
核心中sk_buff結構體做到了可以不使用拷貝或刪除的方式,使得資料在各層協議之間傳輸——即移動指標頭的方式,具體為在處理不同的協議頭時,代表協議頭的指標,指向的是不同資料區域(如從L2到L4層協議,分別指向二層mac頭,三層IP頭,四層傳輸頭)。以下是幾個比較重要和混淆的欄位說明,結合示意圖說明:
指標head/end | 從head指標到end指標區域指向的資料塊為真實儲存以太幀資料區域(包括了鏈路成之上的各層協議協議頭和資料包文,且一直不變) |
指標data/tail | data指標和tail指標表示當前正在處理的協議層的開始和結束為止(其隨著處理協議的向高層/低層推進而變化)head<=data<=tail<=end |
len |
data_len和len比較抽象。len表示skbuff中由head到tail指向的資料塊的大小+分片fragment(即skb_shared_info結構體中)非線性資料大小,其大小會隨著在核心各層中移動而變化(去掉或者增加了各層協議頭) |
data_len |
data_len僅為分片中非線性資料大小。 |
上圖簡單說明了四個指標和指向區域之間的關係。另外對於data_len和len的關係,如果假設icmp報文比較小,ip層不會對其分片,那麼data_len即為0,而len即為當前協議頭長度+資料包文長度。關於data_len和len之間的關係涉及到skb_shared_info這個結構體的相關內容,因為和文章中心關係不大,有興趣的同學可以自行查閱一下文章來學習
- http://blog.51cto.com/weiguozhihui/1586777
- https://0x657573.wordpress.com/2010/11/22/the-relation-between-skb-len-and-skb-data_len-and-what-they-represent/
- https://blog.csdn.net/farmwang/article/details/54233975
上述內容中data指標和表徵協議層資料長度的len,和後文中修改的sk_buff指向的資料直接相關。另外sk_buff關聯了眾多其他結構體,這裡只簡要的講解部分重要的欄位含義,更為具體詳細的說明可以參考Understanding Linux Network Internal第二章或者https://blog.csdn.net/YuZhiHui_No1/article/details/38666589系列文章進行更深入學習。
瞭解了sk_buff結構體,之後需要定位處理icmp協議的檔案在哪裡。icmp.c位於核心目錄中net/ipv4/icmp.c中,且ICMP協議通常是靜態編譯至核心中,而非通過模組配置的。這裡我從Understanding Linux Network Internal這本書中摳出來一張Big Picture,來簡要說明一下對於ping發出的回顯請求,sk_buff結構體物件是如何在icmp中眾多函式中傳遞。
首先ip_local_deliver_finish會傳遞ICMP訊息到icmp_rcv, icmp_rcv會解析icmp報頭中型別欄位,對於屬於查詢報文的型別(如type8)會傳遞給icmp_reply, 而對於差錯報文會傳遞給icmp_send處理,並且ICMP協議也會和其他諸如TCP/UDP協議進行互動傳遞資訊。對於ping程式發出的請求,會先傳遞給icmp_echo函式進行處理。而icmp_echo正是處理ping請求很重要的一步,核心會把請求中附帶的資料包文部分原封不動的拷貝併傳送回源主機。因此我們可以在icmp_echo函式中,新增進我們”愛的語句”。
static bool icmp_echo(struct sk_buff *skb)
{
struct net *net;
net = dev_net(skb_dst(skb)->dev);
if (!net->ipv4.sysctl_icmp_echo_ignore_all) {
struct icmp_bxm icmp_param;
icmp_param.data.icmph = *icmp_hdr(skb);
icmp_param.data.icmph.type = ICMP_ECHOREPLY;
icmp_param.skb = skb;
//-----------新增開始-----------
char sentence1[] = "I LOVE U, xxxx.";
char sentence2[] = "I MISS U, xxxx.";
char sentence3[] = "Happy Valentine`s Day!";
int sentence_len_list[] = {sizeof(sentence1), sizeof(sentence2), sizeof(sentence3)};
char* sentence_list[] = {sentence1, sentence2, sentence3};
int sentence_index = icmp_param.data.icmph.un.echo.sequence % 3;
if(skb->len >= 16 + sentence_len_list[sentence_index])
{
char* tmp = (char*)(skb->data+16);
char* target_sentence = sentence_list[sentence_index];
int i=0;
for(;i<sentence_len_list[sentence_index];++i)
{
tmp[i] = target_sentence[i];
}
for(;i < skb->len-16;++i)
{
tmp[i] = 0;
}
}
//-----------新增結束------------
icmp_param.offset = 0;
icmp_param.data_len = skb->len;
icmp_param.head_len = sizeof(struct icmphdr);
icmp_reply(&icmp_param, skb);
}
/* should there be an ICMP stat for ignored echos? */
return true;
}
上述程式碼中icmp_bxm結構體包含了在後續icmp訊息傳遞過程中的所有需要的資訊,包括icmp報文頭,sk_buff物件,icmp 報文payload大小等。需要注意的是,由於icmp_rcv已經解析過sk_buff中屬於icmp協議的報文頭部分,所以引數中skb->data指向的是icmp資料部分,即不包含報文頭,而skb->len也只有icmp資料部分的長度。假設ping請求中所帶的資料部分為56位元組,則此時skb->len大小為56。由於ping資料部分的前16位元組為攜帶的是傳送是struct timeval物件——傳送時的時間,所以在真實替換時,從data指向的資料部分的第16個位元組開始,用memcpy複製到對應區域,或者如上例子傻傻的迴圈賦值即可。上面程式碼所表示的就是根據echo請求中seq_id迴圈回覆上述三句話。當然有創意的小夥伴可以增加更多表達難度。
四、建立一個阿里雲ECS伺服器,十分鐘完成所有修改
分析完了整個icmp處理流程,和修改方法,我們只需要建立一個阿里雲ECS,並簡單編譯修改後的核心即可。具體流程如下:
- 阿里雲建立任意規格伺服器(大規格可以加快核心編譯速度,此處建立一個4vcpu伺服器),使用centos作為os
- 下載linux核心程式碼,並解壓放置到/usr/src/kernels目錄下,本文使用的是4.20.6核心版本。
- 編譯前基於原centos系統中/boot目錄下的config檔案,生成編譯配置項,根據此編譯項來定製核心。拷貝原配置檔案至核心檔案目錄 sudo cp /boot/config-3.10.0-693.el7.x86_64 ./.config;執行make oldconfig,生成新的.config檔案
- 編譯原始碼:make -j 4 ,可能編譯過程中缺少某些庫,此時yum安裝缺少的庫,如openssl-devel, elfutils-libelf-devel
- 安裝核心模組:make modules_install -j 4
- 拷貝核心和配置檔案至/boot目錄,並生成System.map檔案:make install -j 4
- 更新引導:grub2-mkconfig -o /boot/grub2/grub.cfg
- 修改預設預設啟動引導核心:修改/etc/default/grub檔案,將GRUB_DEFAULT設為0,0表示第一個啟動項,即為最新編譯的核心。
- 重啟伺服器:reboot
至此告訴你的女神/男神,你想說的話都在ping中。
部分參考文章:
- Understanding Linux Network Internal 第2章&第25章
- https://www.geeksforgeeks.org/ping-in-c/
- https://medium.freecodecamp.org/building-and-installing-the-latest-linux-kernel-from-source-6d8df5345980
- https://github.com/iputils/iputils/blob/master/ping.c
相關文章
- 今年的情人節,給心愛的她一個不一樣的禮物吧
- 情人節來了,教你個用 Python 表白的技巧Python
- 這個春節我又過得不一樣了
- 學漢語、來雲棲、海外佈道阿里雲……這位印度架構師不一般阿里架構
- 情人節分手,FreeNginx來了Nginx
- 阿里雲《雲中誰送錦書來》熱烈來襲,給你不一樣的表白體驗阿里
- 2019情人節一個人QQ簽名 一個人的情人節傷感說說句子
- 【父親節H5】用獨特的方式表達最深沉的愛!H5
- 在未來,軟體測試工程師必須熟通這八個技能!工程師
- 這不是就是軟體工程師最愛的東西軟體工程工程師
- 短影片寶貝=慢?阿里巴巴工程師這樣秒開短影片。阿里工程師
- 滲透測試工程師必用的10個工具!工程師
- 當阿里雲工程師回到了家鄉......阿里工程師
- 如何磨練關卡設計師的“手藝”?從這個方向著手試試
- 阿里大資料工程師教你怎樣理解Flume阿里大資料工程師
- 原來,阿里工程師才是隱藏的“修圖高手”!阿里工程師
- 軟體測試工程師的待遇怎麼樣工程師
- 短視訊寶貝=慢?阿里巴巴工程師這樣秒開短視訊。阿里工程師
- 一個測試工程師的養成工程師
- 吳恩達視訊觀後感一(相同的字母表達的意思可是不一樣)吳恩達
- 報告!這群阿里工程師在偷偷養豬阿里工程師
- 疫情下的情人節,疫戰 2020 助你靠譜保護你愛的人
- 2019情人節發微信朋友圈說說 情人節發微信朋友圈情人節句子
- 阿里雲打造未來智慧城市的新模樣!阿里
- 阿里工程師下鄉與一個瓜農的“北伐”阿里工程師
- 【漫畫】程式設計師永遠修不好的Bug——情人節程式設計師
- 3 分鐘生成一個單元測試報告,這個樣式愛了測試報告
- 阿里工程師的“211”情結阿里工程師
- GitHub 工程師:我眼中的理想上司是這樣子的Github工程師
- 阿里巴巴雲原生,在許諾雲端計算一個什麼樣的未來?阿里
- 老程式碼多=過度耦合=if else?阿里巴巴工程師這樣捋直老程式碼阿里工程師
- 英偉達不止晶片,Omniverse可以這樣用來展示公司風采晶片
- 雲主機和雲主機是不一樣的
- oppo、有贊測試開發工程師節選面試題工程師面試題
- 程式設計師的情人節「GitHub 熱點速覽 v.22.07」程式設計師Github
- 不一樣的釋出會不一般的品高雲
- 這40張圖送給單身程式設計師,情人節請一笑而過!程式設計師
- 1024程式設計師的節日,千鋒給你一份不一樣的禮物程式設計師