struct ip_nas_opt { unsigned char code;
unsigned char length;
unsigned char count;
unsigned char reserved;
unsigned long addr;
};
Figure 6 - 28: IP Header Option Data Structure Definition
●
裝置控制 (Device I/O Control)接著我們修該原始碼路徑/usr/src/linux/include 下的檔案 if_tunnel.h,目的是在原始 的通道模組中增加一新的 I/O 命令,其名稱與用途如下表左列:
IOCTL Code Description
SIOCSETTUNNEL 啟用型態 252 之標頭選項並設定內所要放置的 IP 位址
Table 6 - 13: IP-in-IP
HOTunneling Device I/O Control Table
●
通道裝置 (Tunneling Device)最後我們修改原始碼路徑/usr/src/linux/net/ipv4 下的檔案 ipip.c,該檔案便是原始的 IP 承載 IP 通道模組。 第一個步驟為修改該通道裝置在封裝封包時所會讀取的參數,如圖 6-25 所示,在原有的參數最後我們加入了變數 nas_addr 用來存放型態 252 標頭選項內 的 IP 位址。 也就是當用上述的裝置控制命令 SIOCSETTUNNEL 所傳入的位址將會被放 置在該變數內。
struct ip_tunnel_parm
struct iphdr iph;
__u32 nas_addr;
struct iphdr iph;
__u32 nas_addr;
};
Figure 6 - 29: Tunneling Device Parameter
再來就是修改實際執行封包封裝的函式 ipip_tunnel_xmit,圖 6-26 列出了該函式中重 要的部分。 在該函式的處理中,首先檢查上述參數中的 nas_add 變數是否已存入 IP 位 址,若有則預留標頭選項所需的額外空間。 接著在封裝外層標頭的部分也先做同樣的檢 查,若 nas_add 變數已存入 IP 位址,則緊接著外層的 IP 標頭填入上述的資料結構 ip_nas_opt,也就是型態 252 之標頭選項。 並且把 nas_add 變數內的 IP 位址填在該結 構的交換位址欄位 addr。
static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) {...
if(tunnel->parms.nas_addr != INADDR_ANY)
skb->nh.raw = skb_push(skb, sizeof(struct iphdr) + sizeof(struct ip_nas_opt));
else skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
dst_release(skb->dst);
skb->dst = &rt->u.dst;
/** Push down and install the IPIP header.
*/
iph = skb->nh.iph;
iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2;
iph->frag_off = df;
iph->protocol = IPPROTO_IPIP;
iph->tos = INET_ECN_encapsulate(tos, old_iph->tos);
iph->daddr = rt->rt_dst;
iph->saddr = rt->rt_src;
skb->ip_summed = CHECKSUM_NONE;
if(tunnel->parms.nas_addr != INADDR_ANY){
struct ip_nas_opt *nasopt = (struct ip_nas_opt *)((unsigned char *)iph + sizeof(struct iphdr));
iph->ihl += sizeof(struct ip_nas_opt) >> 2;
nasopt->code = 252;
nasopt->length = 8;
nasopt->count = 0;
nasopt->reserved = 0;
nasopt->addr = tunnel->parms.nas_addr;
... } }
static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) {...
if(tunnel->parms.nas_addr != INADDR_ANY)
skb->nh.raw = skb_push(skb, sizeof(struct iphdr) + sizeof(struct ip_nas_opt));
else skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
dst_release(skb->dst);
skb->dst = &rt->u.dst;
/** Push down and install the IPIP header.
*/
iph = skb->nh.iph;
iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2;
iph->frag_off = df;
iph->protocol = IPPROTO_IPIP;
iph->tos = INET_ECN_encapsulate(tos, old_iph->tos);
iph->daddr = rt->rt_dst;
iph->saddr = rt->rt_src;
skb->ip_summed = CHECKSUM_NONE;
if(tunnel->parms.nas_addr != INADDR_ANY){
struct ip_nas_opt *nasopt = (struct ip_nas_opt *)((unsigned char *)iph + sizeof(struct iphdr));
iph->ihl += sizeof(struct ip_nas_opt) >> 2;
nasopt->code = 252;
nasopt->length = 8;
nasopt->count = 0;
nasopt->reserved = 0;
nasopt->addr = tunnel->parms.nas_addr;
... } }
Figure 6 - 30: Tunneling Device Internal Function
HUT Dynamics Mobile IP Package Modification接下來必須修改 Dynamics 套件讓它可以使用我們所開發的 IPHO承載 IP 通道模組。 在這部 分一共修改了套件中的三個檔案,以下分成兩部分說明:
●
通道裝置控制 (Tunneling Device Control)我們在原始碼路徑/dynmacis/src/other 下的檔案 dyn_ip.c 內增加了用來建立 IPHO承 載 IP 通 道 的 函 式 dyn_ip_tunnel_add_nas , 如 下 圖 所 示 。 在 該 函 式 內 所 呼 叫 的 tunnel_ioctl 函式中便以我們所新增的裝置控制命令 SIOCSETTUNNEL 設定型態 252 之 標頭選項內所要填入的 IP 位址。
int dyn_ip_tunnel_add_nas(const char *dev, struct in_addr remote, struct in_addr local, int nas_addr)
{ return tunnel_ioctl(SIOCADDTUNNEL, dev, remote.s_addr, local.s_addr, IPPROTO_IPIP, nas_addr);
}
static int tunnel_ioctl(int cmd, const char *dev, __u32 remote, __u32 local, int proto, __u32 key)
{...
if (proto == IPPROTO_IPIP && key != 0) { p.nas_addr = key;
dynamics_strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = (void*) &p;
if (ioctl(s, SIOCSETTUNNEL, &ifr) != 0) { close(s);
return -1;
} } ...}
int dyn_ip_tunnel_add_nas(const char *dev, struct in_addr remote, struct in_addr local, int nas_addr)
{ return tunnel_ioctl(SIOCADDTUNNEL, dev, remote.s_addr, local.s_addr, IPPROTO_IPIP, nas_addr);
}
static int tunnel_ioctl(int cmd, const char *dev, __u32 remote, __u32 local, int proto, __u32 key)
{...
if (proto == IPPROTO_IPIP && key != 0) { p.nas_addr = key;
dynamics_strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = (void*) &p;
if (ioctl(s, SIOCSETTUNNEL, &ifr) != 0) { close(s);
return -1;
} } ...}
Figure 6 - 31: Dynamics Internal Function I
●
行動連結資訊建立 (Binding Create)這部分牽涉到兩個檔案,首先當行動端註冊時會呼叫原始碼路徑/dynmacis/src/ha 下 之檔案 ha.c 內的 create_binding 函式。 該函式內會呼叫原始碼路徑/dynmacis/src/other 下之檔案 tunnel.c 內負責建立合適的封裝通道模式函式 tunnel_add。 因此,如圖 6-27 所 示修改過後的函式,在呼叫 tunnel_add 函式時我們會將行動端的轉交位址放在最後的參 數欄位內。 而 tunnel_add 函式本身便檢查該參數存在與否呼叫上述用來建立 IPHO承載 IP 通道的函式 dyn_ip_tunnel_add_nas 並把該參數,也就是行動端的轉交位址傳遞下去 作為型態 252 之標頭選項內所要填入的 IP 位址。
static struct bindingentry *
create_binding(struct msg_extensions *ext, struct spi_entry *mn_spi, int create_tunnels, struct sockaddr_in cli_addr)
{...
if (ok && create_tunnels){
if (ext->req->co_addr.s_addr != cli_addr.sin_addr.s_addr){
if (tunnel_add(tunnels, binding->lower_addr, binding->tun_dev,
ext->req->ha_addr, 1, TUNNEL_IPIP, (int)ext->req->co_addr.s_addr) == NULL) { LOG2(LOG_ERR, "Could not add tunnel to FA %s (COA=%s) for "
"MN %s\n", inet_ntoa(binding->lower_addr), co_addrstr, mn_addrstr);
tunnel_add(HASH *hash, struct in_addr dst_addr, char *dev, struct in_addr local_addr, int set_link_up, int type, int key ) {...
if (type == TUNNEL_IPIP) { if(key){
if (dyn_ip_tunnel_add_nas(device, dst_addr, local_addr,key) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add_nas failed!\n");
return NULL;
} } ...
}
static struct bindingentry *
create_binding(struct msg_extensions *ext, struct spi_entry *mn_spi, int create_tunnels, struct sockaddr_in cli_addr)
{...
if (ok && create_tunnels){
if (ext->req->co_addr.s_addr != cli_addr.sin_addr.s_addr){
if (tunnel_add(tunnels, binding->lower_addr, binding->tun_dev,
ext->req->ha_addr, 1, TUNNEL_IPIP, (int)ext->req->co_addr.s_addr) == NULL) { LOG2(LOG_ERR, "Could not add tunnel to FA %s (COA=%s) for "
"MN %s\n", inet_ntoa(binding->lower_addr), co_addrstr, mn_addrstr);
tunnel_add(HASH *hash, struct in_addr dst_addr, char *dev, struct in_addr local_addr, int set_link_up, int type, int key ) {...
if (type == TUNNEL_IPIP) { if(key){
if (dyn_ip_tunnel_add_nas(device, dst_addr, local_addr,key) != 0) { DEBUG(DEBUG_FLAG, "\tdyn_ip_tunnel_add_nas failed!\n");
return NULL;
} } ...
}
Figure 6 - 32: Dynamics Internal Function II
Network Address Swapping Function在 Linux 的系統中,netfilter 是一個用來做封包擷取的架構,它在封包通過協定堆疊的途中定 義了許多攔截點(hook). 以 IPv4 來說,封包的處理流程與經過的攔截點如圖 6-29 所示。
Figure 6 - 33: IPv4 Traversal Diagram
iptables 便是建立在 netfilter 架構之上的封包篩選系統。 核心模組可以註冊新的 table 並要求 封包經過該 table。 封包篩選的方法可用作封包的過濾(filter table)與網路位址轉譯(nat table)等。
對於後者來說,便是利用攔截點 NF_IP_PRE_ROUTING 與 NF_IP_POST_ROUTING 執行非本 機所產生之封包的轉譯目的與來源 IP 位址。 因此,要實作我們所提出的網路位址交換模組便必須 修改這兩個攔截點的處理函式。 以下便介紹對此二攔截點的修改:
●
資料結構 (Data Structure)為了讓網路位址轉譯器認得我們所定義的 IP 標頭選項,我們同樣必須在系統中定義 其資料結構。 因此,在核心原始碼的路徑/usr/src/linux/include/linux/netfilter_ipv4 下,我 們加入了如同圖 6-24 之資料結構的檔案 ip_nas.h。
manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, const struct ip_conntrack_manip *manip,
enum ip_nat_manip_type maniptype, __u32 *nfcache)
{...
if (maniptype == IP_NAT_MANIP_SRC) { if (iph->ihl > 5) {
struct ip_nat_as_opt *aso = (struct ip_nat_as_opt *)((u_int32_t *)iph + 5);
if (aso->code == IPOPT_AS && aso->length >= 8 && aso->addr == INADDR_ANY) { u_int32_t oldval = *(u_int32_t *)aso;
iph->check = ip_nat_cheat_check(INADDR_NONE, manip->ip, iph->check);
aso->addr = iph->saddr;
iph->saddr = manip->ip;
aso->count++;
iph->check = ip_nat_cheat_check(~oldval, *(u_int32_t *)aso, iph->check);
} else {}
iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, iph->check);
iph->saddr = manip->ip;
} else {}
if (iph->ihl > 5) {
struct ip_nat_as_opt *aso = (struct ip_nat_as_opt *)((u_int32_t *)iph + 5);
if (aso->code == IPOPT_AS && aso->length >= 8 && aso->addr == manip->ip) { u_int32_t oldval = *(u_int32_t *)aso;
aso->addr = iph->daddr;
iph->daddr = manip->ip;
aso->count++;
iph->check = ip_nat_cheat_check(~oldval, *(u_int32_t *)aso, iph->check);
} else {}
iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip, iph->check);
iph->daddr = manip->ip;
} } ...
}
static void
manip_pkt(u_int16_t proto, struct iphdr *iph, size_t len, const struct ip_conntrack_manip *manip,
enum ip_nat_manip_type maniptype, __u32 *nfcache)
{...
if (maniptype == IP_NAT_MANIP_SRC) { if (iph->ihl > 5) {
struct ip_nat_as_opt *aso = (struct ip_nat_as_opt *)((u_int32_t *)iph + 5);
if (aso->code == IPOPT_AS && aso->length >= 8 && aso->addr == INADDR_ANY) { u_int32_t oldval = *(u_int32_t *)aso;
iph->check = ip_nat_cheat_check(INADDR_NONE, manip->ip, iph->check);
aso->addr = iph->saddr;
iph->saddr = manip->ip;
aso->count++;
iph->check = ip_nat_cheat_check(~oldval, *(u_int32_t *)aso, iph->check);
} else {}
iph->check = ip_nat_cheat_check(~iph->saddr, manip->ip, iph->check);
iph->saddr = manip->ip;
} else {}
if (iph->ihl > 5) {
struct ip_nat_as_opt *aso = (struct ip_nat_as_opt *)((u_int32_t *)iph + 5);
if (aso->code == IPOPT_AS && aso->length >= 8 && aso->addr == manip->ip) { u_int32_t oldval = *(u_int32_t *)aso;
aso->addr = iph->daddr;
iph->daddr = manip->ip;
aso->count++;
iph->check = ip_nat_cheat_check(~oldval, *(u_int32_t *)aso, iph->check);
} else {}
iph->check = ip_nat_cheat_check(~iph->daddr, manip->ip, iph->check);
iph->daddr = manip->ip;
} } ...
}
Figure 6 - 34: NAT Internal Function
6.2.3 Software Demonstration
在本節的最後我們將以實際的程式展示 5.4.3 的 IPHO承載 IP 通道模式,並用 ICMP 回應請求/回應回覆訊息做為實例。
Software Configuration●
家代理器 (Home Agent)在家代理器的部分必須先載入 IP-in-IPHO的封裝模組後再啟動家代理器的主程式,如 圖 6-31 所示。
Figure 6 - 35: Home Agent Configuration Snapshot
●
行動端 (Mobile Node)行動端的部分則是在執行主程式的時候指定不使用無線網路的延伸功能並先啟始在 離線的狀態。 接著以用來控制主程式運作的公用程式 dynmn_tool 指定行動端使用配置轉 交位址模式向家代理器註冊,如下圖所示。
Figure 6 - 36: Mobile Node Configuration Snapshot
Program Snapshots承上,當完成註冊後我們由行動端向通訊端發送一個 ICMP 的回應請求訊息,然後檢視該封 包在經過網路位址轉譯器時的處理。 如圖 6-33 所示,左邊的為網路位址轉譯器對外網路卡 eth0 上之封包擷取,而右邊的則為對內網路卡 eth1 上之封包擷取。 誠如 5.4.3 的敘述,該封包首先經 過 IP 承載 IP 的封裝後送出於對內的網路卡上,也就是下圖右方的第一個封包。 由該封包的內容 我們可以注意到行動端目前的配置轉交位址為 10.113.215.181 (0x0a71d7b5)。 當封包經過位址 轉譯後便出現在對外的網路卡上,也就是下圖左方的第一個封包,此時該封包的來源位址已由原 本的行動端配置轉交位址換為網路位址轉譯器的公有 IP 位址 140.113.17.15 (0x8c71110f)。 若干 時間後,當家代理器收到通訊端的 ICMP 的回應回覆訊息,便以 IPHO承載 IP 的封裝方式將該封包 送出,而該封包便回到網路位址轉譯器的對外網路卡上,也就是下圖左方的第二個封包。 同樣由 該封包的內容我們可以注意到在外層的 IP 標頭部分多了型態 252 的標頭選項,而且裡面所放置的 IP 位址為行動端目前的配置轉交位址。 最後,當該封包出現在對內的網路卡上時,也就是下圖右 方的第二個封包,型態 252 標頭選項內的 IP 位址已與外層 IP 標頭的目的 IP 位址交換,完全符合 我們所預期的結果。 因此,行動端便順利地得到該 ICMP 的回應回覆訊息(見圖 6-32),同時也驗 證了我們所提出方法的可行性。
Figure 6 - 37: Packet Dump on External /Internal Interface
Chapter 7 Discussion and Remark
Section 7.1 Multi-Interface Integration Alternatives
在第四章中我們已經詳細的介紹實作整合異質網路平台所需的軟體元件與其所執行的功能。
在本節中將就行動端家位址之設定,封包之封裝與鏈結層資訊之取得等議題探討是否有其它的解 決方案並比較其與我們所提出方法的優缺點。
7.1.1 Home IP Address Configuration
由於我們是以鬆散連結的整合架構來整合異質網路,因此必須靠行動網際網路協定來支援漫 遊時的行動管理。 而根據該協定行動端必須隨時皆使用家位址與其它的通訊端建立連線方可避免 切換網路時所造成的連線中斷。 換句話說,行動端的家位址必須隨時地設定於行動端的某張網路 卡上供網路應用程式來使用。 我們的作法是以 IP 別名的技巧將家位址加在主配接卡上,但這樣的 方法會遭遇到的問題就是媒體感應事件會造成該 IP 位址的遺失。 於是我們便以 Mobile IP 服務中 間層驅動程式來克服這樣的問題。
不過,我們也可考慮以 DDK 所提供的範例程式”虛擬迷你連接埠驅動程式 (Virtual Miniport Driver)”來解決該問題。 就如同它的名稱,該驅動程式主要是實作了一張虛擬的網路卡,由於缺乏 實體的硬體裝置,原本的媒體感應事件自然不復存在。 所以我們便可利用這樣的特性,將行動端 的家位址設定在該網路卡上,這樣一來便不再需要 Mobile IP 服務中間層驅動程式。 然而,使用
不過,我們也可考慮以 DDK 所提供的範例程式”虛擬迷你連接埠驅動程式 (Virtual Miniport Driver)”來解決該問題。 就如同它的名稱,該驅動程式主要是實作了一張虛擬的網路卡,由於缺乏 實體的硬體裝置,原本的媒體感應事件自然不復存在。 所以我們便可利用這樣的特性,將行動端 的家位址設定在該網路卡上,這樣一來便不再需要 Mobile IP 服務中間層驅動程式。 然而,使用