## 17.1.?snull 是如何設計的
本節談論產生 snull 網絡接口的設計概念. 盡管這個信息可能看來是邊緣的使用, 不理解它在你運行例子代碼時可能會導致問題.
首先, 也是最重要的, 設計的決定是例子接口應該保持獨立于真實的硬件, 就像本書使用的大部分例子. 這個限制導致了一些構成環回接口的東西. snull 不是一個環回接口; 但是, 它模擬了與真實的遠端主機間的對話, 以便更好演示編寫一個網絡驅動的任務. Linux 環回驅動實際是非常簡單的; 它可在 drivers/net/lookback.c 找到.
snull 的另一個特性是它只支持 IP 通訊. 這是接口的內部工作的結果 -- snull 不得不查看里面并且解析報文來正確模擬一對硬件接口. 實際的接口不依賴于被發送的協議, 并且 snull 的這種限制不影響本章展示的代碼片斷.
### 17.1.1.?分配 IP 號
snull 模塊創建了兩個接口. 這些接口與一個簡單的環回不同, 因為無論你通過其中一個接口發送什么都環回到另外一個, 而不是它自己. 它看起來好像你有兩個外部連接, 但實際上是你的計算機在回答它自己.
不幸的是, 這個效果不能僅僅通過 IP 號碼分配來完成, 因為內核不會通過接口 A 發送一個報文給它自己的接口 B, 它會利用環回通道而不是通過 snull. 為了能建立一個通過 snull 接口的通訊, 源和目的地址在實際傳送中需要修改. 換句話說, 通過其中一個接口發送的報文應該被另一個收到, 但是外出報文的接受者不應當被認做是本地主機. 同樣適用于接收到的報文的源地址.
為獲得這種"隱藏的環回", snull 接口翻轉源地址和目的地址的第 3 個 octet 的最低有效位; 就是說, 它改變了 C 類 IP 編號的網絡編號和主機編號. 網絡方面的效果是發給網絡 A( 連接在 sn0 上, 第一個接口 )的報文作為屬于網絡 B 的報文出現在 sn1 接口.
為避免處理太多編號, 我們分配符號名子給涉及到的 IP 編號:
-
snullnet0 是連接到 sn0 接口的網絡. 同樣, snullnet1 是連接到 sn1. 這些網絡的地址應當僅僅在第 3 個 octet 的最低有效位不同. 這些網絡必須有 24 位的子網掩碼.
-
local0 是分配給 sn0 接口的 IP 地址; 它屬于 snullnet0. 陪伴 sn1 的地址是 local1. local0 和 local1 必須在它們的第 3 octet 的最低有效位和第 4 octet 上不同.
-
remote0 是在 snullnet0 的主機, 并且它的第 4 octet 與 local1 的相同. 任何發送給 remote0 的報文到達 local1, 在它的網絡地址被接口代碼改變之后. 主機 remote1 屬于 snullnet1, 它的第 4 octet 與 local0 相同.
snull 接口的操作在圖 [主機如何看它的接口](# "圖?17.1.?主機如何看它的接口")中描述, 其中每個接口的關聯的主機名印在接口名的旁邊.
**圖?17.1.?主機如何看它的接口**

下面是網絡編號的可能值. 一旦你把這些行放進 /etc/networks, 你可以使用名子來調用你的網絡. 這些值選自保留做私人用途的編號范圍.
~~~
snullnet0 192.168.0.0
snullnet1 192.168.1.0
~~~
下面的是一些可能的主機編號, 可放進 /etc/hosts 里面:
~~~
192.168.0.1 local0
192.168.0.2 remote0
192.168.1.2 local1
192.168.1.1 remote1
~~~
這些編號的重要特性是 local0 的主機部分與 remote1 的主機部分相同, local1 的主機部分和 remote0 的主機部分相同. 你可以使用完全不同的編號, 只要保持著這種關系.
但是要小心, 如果你的計算機以及連接到一個網絡上. 你選擇的編號可能是真實的互聯網或者內聯網的編號, 把它們安排給你的接口會阻止和這些真實的主機間的通訊. 例如, 盡管剛剛展示的這些編號不是可以路由的互聯網編號, 它們也可能被你的私有網絡已經在使用.
不管你選擇什么編號, 你可以正確設置這些接口來操作, 通過發出下面的命令:
~~~
ifconfig sn0 local0
ifconfig sn1 local1
~~~
你可能需要添加網絡掩碼 255.255.255.0 參數, 如果選擇的地址范圍不是 C 類范圍.
在此, 接口的"遠程"端點能夠到達了. 下面的屏幕拷貝顯示了一個主機如何到達 remote0 和 remote1 的, 通過 snull 接口.
~~~
morgana% ping -c 2 remote0
64 bytes from 192.168.0.99: icmp_seq=0 ttl=64 time=1.6 ms
64 bytes from 192.168.0.99: icmp_seq=1 ttl=64 time=0.9 ms
2 packets transmitted, 2 packets received, 0% packet loss
morgana% ping -c 2 remote1
64 bytes from 192.168.1.88: icmp_seq=0 ttl=64 time=1.8 ms
64 bytes from 192.168.1.88: icmp_seq=1 ttl=64 time=0.9 ms
2 packets transmitted, 2 packets received, 0% packet loss
~~~
注意, 你不能到達屬于這兩個網絡的任何其他主機, 因為報文被你的計算機丟棄了, 在地址被修改和收到報文之后. 例如, 一個發向 192.168.0.32 的報文將離開 sn0 并以 192.168.1.32 的目的地址出現在 sn1, 這并不是這臺主機的本地地址.
### 17.1.2.?報文的物理傳送
只考慮數據傳送的話, snull 接口屬于以太網一類的.
snull 模擬以太網是因為大量的現存網絡 -- 至少一個工作站所連接的網段 -- 是基于以太網技術的, 它可能是 10base-T, 100base-T, 或者 千兆網. 另外, 內核為以太網設備提供了一些通用的接口, 沒有理由不用它. 一個以太網設備的優勢是如此的強以至于 plip 接口( 使用打印機端口的接口 )都聲明自己是一個以太網設備.
snull 使用以太網設置的最后一個優勢是你可以運行 tcpdump 在接口上來觀察過往的報文. 使用 tcpdump 來觀察接口是得知兩個接口如何工作的有用途徑.
如同我們之前提到的, snull 只處理 IP 報文. 這個限制來自這樣的事實, snull 監聽報文并且甚至修改它們, 以便使代碼工作. 代碼修改了每個報文的源, 目的和 IP header 的校驗和, 并不檢查它是否實際承載著 IP 信息.
這種快而臟的數據修改毀壞了非 IP 報文. 如果你想通過 snull 遞交其他協議, 你必須修改模塊的源代碼.
- Linux設備驅動第三版
- 第 1 章 設備驅動簡介
- 1.1. 驅動程序的角色
- 1.2. 劃分內核
- 1.3. 設備和模塊的分類
- 1.4. 安全問題
- 1.5. 版本編號
- 1.6. 版權條款
- 1.7. 加入內核開發社團
- 1.8. 本書的內容
- 第 2 章 建立和運行模塊
- 2.1. 設置你的測試系統
- 2.2. Hello World 模塊
- 2.3. 內核模塊相比于應用程序
- 2.4. 編譯和加載
- 2.5. 內核符號表
- 2.6. 預備知識
- 2.7. 初始化和關停
- 2.8. 模塊參數
- 2.9. 在用戶空間做
- 2.10. 快速參考
- 第 3 章 字符驅動
- 3.1. scull 的設計
- 3.2. 主次編號
- 3.3. 一些重要數據結構
- 3.4. 字符設備注冊
- 3.5. open 和 release
- 3.6. scull 的內存使用
- 3.7. 讀和寫
- 3.8. 使用新設備
- 3.9. 快速參考
- 第 4 章 調試技術
- 4.1. 內核中的調試支持
- 4.2. 用打印調試
- 4.3. 用查詢來調試
- 4.4. 使用觀察來調試
- 4.5. 調試系統故障
- 4.6. 調試器和相關工具
- 第 5 章 并發和競爭情況
- 5.1. scull 中的缺陷
- 5.2. 并發和它的管理
- 5.3. 旗標和互斥體
- 5.4. Completions 機制
- 5.5. 自旋鎖
- 5.6. 鎖陷阱
- 5.7. 加鎖的各種選擇
- 5.8. 快速參考
- 第 6 章 高級字符驅動操作
- 6.1. ioctl 接口
- 6.2. 阻塞 I/O
- 6.3. poll 和 select
- 6.4. 異步通知
- 6.5. 移位一個設備
- 6.6. 在一個設備文件上的存取控制
- 6.7. 快速參考
- 第 7 章 時間, 延時, 和延后工作
- 7.1. 測量時間流失
- 7.2. 獲知當前時間
- 7.3. 延后執行
- 7.4. 內核定時器
- 7.5. Tasklets 機制
- 7.6. 工作隊列
- 7.7. 快速參考
- 第 8 章 分配內存
- 8.1. kmalloc 的真實故事
- 8.2. 后備緩存
- 8.3. get_free_page 和其友
- 8.4. 每-CPU 的變量
- 8.5. 獲得大量緩沖
- 8.6. 快速參考
- 第 9 章 與硬件通訊
- 9.1. I/O 端口和 I/O 內存
- 9.2. 使用 I/O 端口
- 9.3. 一個 I/O 端口例子
- 9.4. 使用 I/O 內存
- 9.5. 快速參考
- 第 10 章 中斷處理
- 10.1. 準備并口
- 10.2. 安裝一個中斷處理
- 10.3. 前和后半部
- 10.4. 中斷共享
- 10.5. 中斷驅動 I/O
- 10.6. 快速參考
- 第 11 章 內核中的數據類型
- 11.1. 標準 C 類型的使用
- 11.2. 安排一個明確大小給數據項
- 11.3. 接口特定的類型
- 11.4. 其他移植性問題
- 11.5. 鏈表
- 11.6. 快速參考
- 第 12 章 PCI 驅動
- 12.1. PCI 接口
- 12.2. 回顧: ISA
- 12.3. PC/104 和 PC/104+
- 12.4. 其他的 PC 總線
- 12.5. SBus
- 12.6. NuBus 總線
- 12.7. 外部總線
- 12.8. 快速參考
- 第 13 章 USB 驅動
- 13.1. USB 設備基礎知識
- 13.2. USB 和 sysfs
- 13.3. USB 的 Urbs
- 13.4. 編寫一個 USB 驅動
- 13.5. 無 urb 的 USB 傳送
- 13.6. 快速參考
- 第 14 章 Linux 設備模型
- 14.1. Kobjects, Ksets 和 Subsystems
- 14.2. 低級 sysfs 操作
- 14.3. 熱插拔事件產生
- 14.4. 總線, 設備, 和驅動
- 14.5. 類
- 14.6. 集成起來
- 14.7. 熱插拔
- 14.8. 處理固件
- 14.9. 快速參考
- 第 15 章 內存映射和 DMA
- 15.1. Linux 中的內存管理
- 15.2. mmap 設備操作
- 15.3. 進行直接 I/O
- 15.4. 直接內存存取
- 15.5. 快速參考
- 第 16 章 塊驅動
- 16.1. 注冊
- 16.2. 塊設備操作
- 16.3. 請求處理
- 16.4. 一些其他的細節
- 16.5. 快速參考
- 第 17 章 網絡驅動
- 17.1. snull 是如何設計的
- 17.2. 連接到內核
- 17.3. net_device 結構的詳情
- 17.4. 打開與關閉
- 17.5. 報文傳送
- 17.6. 報文接收
- 17.7. 中斷處理
- 17.8. 接收中斷緩解
- 17.9. 連接狀態的改變
- 17.10. Socket 緩存
- 17.11. MAC 地址解析
- 17.12. 定制 ioctl 命令
- 17.13. 統計信息
- 17.14. 多播
- 17.15. 幾個其他細節
- 17.16. 快速參考
- 第 18 章 TTY 驅動
- 18.1. 一個小 TTY 驅動
- 18.2. tty_driver 函數指針
- 18.3. TTY 線路設置
- 18.4. ioctls 函數
- 18.5. TTY 設備的 proc 和 sysfs 處理
- 18.6. tty_driver 結構的細節
- 18.7. tty_operaions 結構的細節
- 18.8. tty_struct 結構的細節
- 18.9. 快速參考