ip_vs实现分析(7)
生活随笔
收集整理的這篇文章主要介紹了
ip_vs实现分析(7)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本文檔的Copyleft歸yfydz所有,使用GPL發(fā)布,可以自由拷貝,轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)保持文檔的完整性,嚴(yán)禁用于任何商業(yè)用途。
msn:?yfydz_no1@hotmail.com
來(lái)源:http://yfydz.cublog.cn
IPVS的應(yīng)用是針對(duì)象FTP等的多連接協(xié)議處理的,由于多連接協(xié)議的特殊性,任何以連接為基礎(chǔ)進(jìn)行處理的模塊如IPVS,netfilter等都必須對(duì)這些協(xié)議特別處理,不過(guò)IPVS相對(duì)沒(méi)有netfilter那么完善,目前也僅僅支持FTP協(xié)議,而netfilter已經(jīng)可以支持FTP、TFTP、IRC、AMANDA、MMS、SIP、H.323等多種多連接協(xié)議。
IPVS應(yīng)用也是模塊化的,不過(guò)其實(shí)現(xiàn)有點(diǎn)特別,對(duì)于每一個(gè)應(yīng)用協(xié)議,會(huì)定義一個(gè)靜態(tài)的struct ip_vs_app結(jié)構(gòu)作為模板,以后登記該協(xié)議時(shí),對(duì)應(yīng)的應(yīng)用指針并不是直接指向這個(gè)靜態(tài)結(jié)構(gòu),而是新分配一個(gè)struct ip_vs_app結(jié)構(gòu),結(jié)構(gòu)中的struct ip_vs_app指針指向這個(gè)靜態(tài)結(jié)構(gòu),然后把新分配的這個(gè)結(jié)構(gòu)分別掛接到靜態(tài)struct ip_vs_app結(jié)構(gòu)的具體實(shí)現(xiàn)鏈表和IP協(xié)議的應(yīng)用HASH鏈表中進(jìn)行使用,這種實(shí)現(xiàn)方法和netfilter完全不同。
IPVS應(yīng)用一些共享的處理函數(shù)在net/ipv4/ipvs/ip_vs_app.c中定義,其他各協(xié)議相關(guān)處理分別由各自文件處理,如net/ipv4/ipvs/ip_vs_ftp.c.
9.1 新建應(yīng)用結(jié)構(gòu) /*
?*?Allocate/initialize app incarnation and register it in proto apps.
?*/
// 新建一個(gè)應(yīng)用實(shí)例,注意輸入?yún)?shù)除了協(xié)議端口外,還需要提供一個(gè)應(yīng)用模板的指針
// 而且函數(shù)并不直接返回應(yīng)用結(jié)構(gòu)本身,而是在函數(shù)中新建的應(yīng)用實(shí)例直接掛接到鏈表中
// 只返回建立成功(0)或失敗(<0)
static int
ip_vs_app_inc_new(struct ip_vs_app *app, __u16 proto, __u16 port)
{
?struct ip_vs_protocol *pp;
?struct ip_vs_app *inc;
?int ret; // 查找IPVS協(xié)議結(jié)構(gòu)
?if (!(pp = ip_vs_proto_get(proto)))
??return -EPROTONOSUPPORT; // 協(xié)議中不能只有應(yīng)用登記函數(shù)而沒(méi)有拆除函數(shù)
?if (!pp->unregister_app)
??return -EOPNOTSUPP; // 分配 應(yīng)用結(jié)構(gòu)內(nèi)存
?inc = kmalloc(sizeof(struct ip_vs_app), GFP_KERNEL);
?if (!inc)
??return -ENOMEM;
// 將應(yīng)用模板中的內(nèi)容全部拷貝到新應(yīng)用結(jié)構(gòu)中
?memcpy(inc, app, sizeof(*inc));
// 所有應(yīng)用鏈表
?INIT_LIST_HEAD(&inc->p_list);
// 應(yīng)用實(shí)例鏈表
?INIT_LIST_HEAD(&inc->incs_list);
// 應(yīng)用實(shí)例中指向模板本身的指針
?inc->app = app;
// 應(yīng)用協(xié)議的端口
?inc->port = htons(port);
// 實(shí)例的使用計(jì)數(shù)
?atomic_set(&inc->usecnt, 0); if (app->timeouts) {
// 建立應(yīng)用協(xié)議狀態(tài)超時(shí)數(shù)組
??inc->timeout_table =
???ip_vs_create_timeout_table(app->timeouts,
???????? app->timeouts_size);
??if (!inc->timeout_table) {
???ret = -ENOMEM;
???goto out;
??}
?}
// 將應(yīng)用實(shí)例向IP協(xié)議結(jié)構(gòu)登記
?ret = pp->register_app(inc);
?if (ret)
??goto out; // 將應(yīng)用實(shí)例添加到應(yīng)用模板的實(shí)例鏈表
?list_add(&inc->a_list, &app->incs_list);
?IP_VS_DBG(9, "%s application %s:%u registered\n",
??? pp->name, inc->name, inc->port); return 0; out:
?kfree(inc->timeout_table);
?kfree(inc);
?return ret;
}
9.2 釋放應(yīng)用結(jié)構(gòu)
/*
?*?Release app incarnation
?*/
static void
ip_vs_app_inc_release(struct ip_vs_app *inc)
{
?struct ip_vs_protocol *pp; // 找協(xié)議結(jié)構(gòu)指針
?if (!(pp = ip_vs_proto_get(inc->protocol)))
??return; // 調(diào)用協(xié)議的應(yīng)用拆除函數(shù),這里應(yīng)該不用進(jìn)行判斷的
?if (pp->unregister_app)
??pp->unregister_app(inc); IP_VS_DBG(9, "%s App %s:%u unregistered\n",
??? pp->name, inc->name, inc->port);
// 從所有應(yīng)用鏈表中刪除
?list_del(&inc->a_list); // 釋放超時(shí)表,這里怎么不檢查timeout_table是否存在呢?
// 分配應(yīng)用實(shí)例的時(shí)候有可能不分配的
?kfree(inc->timeout_table);
// 釋放結(jié)構(gòu)本身
?kfree(inc);
} 9.3 登記應(yīng)用
/*
?*?Register an application incarnation in protocol applications
?*/
// 登記應(yīng)用實(shí)例,新建并登記
int
register_ip_vs_app_inc(struct ip_vs_app *app, __u16 proto, __u16 port)
{
?int result; mutex_lock(&__ip_vs_app_mutex);
// 新生成一個(gè)應(yīng)用實(shí)例并進(jìn)行登記
?result = ip_vs_app_inc_new(app, proto, port); mutex_unlock(&__ip_vs_app_mutex); return result;
}
/*
?*?ip_vs_app registration routine
?*/
// 登記應(yīng)用, 只登記
int register_ip_vs_app(struct ip_vs_app *app)
{
?/* increase the module use count */
?ip_vs_use_count_inc(); mutex_lock(&__ip_vs_app_mutex);
// 直接將應(yīng)用掛接到IPVS的應(yīng)用鏈表中
?list_add(&app->a_list, &ip_vs_app_list); mutex_unlock(&__ip_vs_app_mutex); return 0;
}
9.4 拆除應(yīng)用 /*
?*?ip_vs_app unregistration routine
?*?We are sure there are no app incarnations attached to services
?*/
void unregister_ip_vs_app(struct ip_vs_app *app)
{
?struct ip_vs_app *inc, *nxt; mutex_lock(&__ip_vs_app_mutex); list_for_each_entry_safe(inc, nxt, &app->incs_list, a_list) {
??ip_vs_app_inc_release(inc);
?} list_del(&app->a_list); mutex_unlock(&__ip_vs_app_mutex); /* decrease the module use count */
?ip_vs_use_count_dec();
}
9.5 應(yīng)用與連接的綁定和拆除
/*
?*?Bind ip_vs_conn to its ip_vs_app (called by cp constructor)
?*/
int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp)
{
// 直接調(diào)用協(xié)議的app_conn_bind函數(shù)處理,通過(guò)連接端口查找應(yīng)用
?return pp->app_conn_bind(cp);
}
/*
?*?Unbind cp from application incarnation (called by cp destructor)
?*/
void ip_vs_unbind_app(struct ip_vs_conn *cp)
{
?struct ip_vs_app *inc = cp->app; if (!inc)
??return; // 分別調(diào)用應(yīng)用實(shí)例的unbind_conn和done_conn函數(shù)拆除連接中應(yīng)用的綁定
?if (inc->unbind_conn)
??inc->unbind_conn(inc, cp);
?if (inc->done_conn)
??inc->done_conn(inc, cp);
?ip_vs_app_inc_put(inc);
?cp->app = NULL;
}
9.6 處理輸出方向的應(yīng)用數(shù)據(jù)
應(yīng)用協(xié)議修改輸出方向的應(yīng)用層數(shù)據(jù),在協(xié)議的snat_handler()函數(shù)中調(diào)用 /*
?*?Output pkt hook. Will call bound ip_vs_app specific function
?*?called by ipvs packet handler, assumes previously checked cp!=NULL
?*?returns false if it can't handle packet (oom)
?*/
int ip_vs_app_pkt_out(struct ip_vs_conn *cp, struct sk_buff **pskb)
{
?struct ip_vs_app *app; /*
? *?check if application module is bound to
? *?this ip_vs_conn.
? */
// 檢查連接是否和應(yīng)用綁定
?if ((app = cp->app) == NULL)
??return 1; /* TCP is complicated */
?if (cp->protocol == IPPROTO_TCP)
// TCP協(xié)議另外單獨(dú)處理
??return app_tcp_pkt_out(cp, pskb, app); /*
? *?Call private output hook function
? */
?if (app->pkt_out == NULL)
??return 1;
// 非TCP協(xié)議調(diào)用應(yīng)用協(xié)議的pkt_out()函數(shù)
?return app->pkt_out(app, cp, pskb, NULL);
} // 處理TCP應(yīng)用發(fā)出方向的數(shù)據(jù)包
static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff **pskb,
????? struct ip_vs_app *app)
{
?int diff;
// 現(xiàn)在就計(jì)算偏移值有點(diǎn)危險(xiǎn),最好在數(shù)據(jù)包可寫(xiě)操作完再設(shè)
?unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4;
?struct tcphdr *th;
?__u32 seq; // 首先要讓數(shù)據(jù)包可寫(xiě)
?if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th)))
??return 0; th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset); /*
? *?Remember seq number in case this pkt gets resized
? */
// 當(dāng)前的序列號(hào)
?seq = ntohl(th->seq); /*
? *?Fix seq stuff if flagged as so.
? */
?if (cp->flags & IP_VS_CONN_F_OUT_SEQ)
// 修改發(fā)出方向序列號(hào)
??vs_fix_seq(&cp->out_seq, th);
?if (cp->flags & IP_VS_CONN_F_IN_SEQ)
// 修改進(jìn)入方向序列號(hào)
??vs_fix_ack_seq(&cp->in_seq, th); /*
? *?Call private output hook function
? */
?if (app->pkt_out == NULL)
??return 1; // 調(diào)用應(yīng)用協(xié)議的pkt_out()函數(shù)
?if (!app->pkt_out(app, cp, pskb, &diff))
??return 0; /*
? *?Update ip_vs seq stuff if len has changed.
? */
?if (diff != 0)
// 數(shù)據(jù)長(zhǎng)度發(fā)生變化,再次修改發(fā)出方向的序列號(hào)
??vs_seq_update(cp, &cp->out_seq,
???????? IP_VS_CONN_F_OUT_SEQ, seq, diff); return 1;
}
9.6 處理進(jìn)入方向的應(yīng)用數(shù)據(jù)
應(yīng)用協(xié)議修改進(jìn)入方向的應(yīng)用層數(shù)據(jù),在協(xié)議的dnat_handler()函數(shù)中調(diào)用
/*
?*?Input pkt hook. Will call bound ip_vs_app specific function
?*?called by ipvs packet handler, assumes previously checked cp!=NULL.
?*?returns false if can't handle packet (oom).
?*/
int ip_vs_app_pkt_in(struct ip_vs_conn *cp, struct sk_buff **pskb)
{
?struct ip_vs_app *app; /*
? *?check if application module is bound to
? *?this ip_vs_conn.
? */
// 檢查連接是否和應(yīng)用綁定
?if ((app = cp->app) == NULL)
??return 1; /* TCP is complicated */
?if (cp->protocol == IPPROTO_TCP)
// TCP協(xié)議另外單獨(dú)處理
??return app_tcp_pkt_in(cp, pskb, app); /*
? *?Call private input hook function
? */
?if (app->pkt_in == NULL)
??return 1; // 非TCP協(xié)議調(diào)用應(yīng)用協(xié)議的pkt_out()函數(shù)
?return app->pkt_in(app, cp, pskb, NULL);
} // 處理TCP應(yīng)用進(jìn)入方向的數(shù)據(jù)包
static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff **pskb,
???? struct ip_vs_app *app)
{
?int diff;
// 現(xiàn)在就計(jì)算偏移值有點(diǎn)危險(xiǎn),最好在數(shù)據(jù)包可寫(xiě)操作完再設(shè)
?unsigned int tcp_offset = (*pskb)->nh.iph->ihl*4;
?struct tcphdr *th;
?__u32 seq; // 首先要讓數(shù)據(jù)包可寫(xiě)
?if (!ip_vs_make_skb_writable(pskb, tcp_offset + sizeof(*th)))
??return 0; th = (struct tcphdr *)((*pskb)->nh.raw + tcp_offset); /*
? *?Remember seq number in case this pkt gets resized
? */
// 當(dāng)前的序列號(hào)
?seq = ntohl(th->seq); /*
? *?Fix seq stuff if flagged as so.
? */
?if (cp->flags & IP_VS_CONN_F_IN_SEQ)
// 修改進(jìn)入方向序列號(hào)
??vs_fix_seq(&cp->in_seq, th);
?if (cp->flags & IP_VS_CONN_F_OUT_SEQ)
// 修改發(fā)出方向序列號(hào)
??vs_fix_ack_seq(&cp->out_seq, th); /*
? *?Call private input hook function
? */
?if (app->pkt_in == NULL)
??return 1; // 調(diào)用應(yīng)用協(xié)議的pkt_in()函數(shù)
?if (!app->pkt_in(app, cp, pskb, &diff))
??return 0; /*
? *?Update ip_vs seq stuff if len has changed.
? */
?if (diff != 0)
// 數(shù)據(jù)長(zhǎng)度發(fā)生變化,再次修改輸入方向的序列號(hào)
??vs_seq_update(cp, &cp->in_seq,
???????? IP_VS_CONN_F_IN_SEQ, seq, diff); return 1;
}
9.7 修改數(shù)據(jù)包中內(nèi)容
將skb包中某段數(shù)據(jù)更改為新的數(shù)據(jù),是一個(gè)通用函數(shù),可供應(yīng)用協(xié)議修改協(xié)議數(shù)據(jù)的函數(shù)調(diào)用,類似于netfilter的mangle_contents()函數(shù). /*
?*?Replace a segment of data with a new segment
?*/
int ip_vs_skb_replace(struct sk_buff *skb, gfp_t pri,
??????? char *o_buf, int o_len, char *n_buf, int n_len)
{
?struct iphdr *iph;
?int diff;
?int o_offset;
?int o_left; EnterFunction(9); // 新數(shù)據(jù)和老數(shù)據(jù)的長(zhǎng)度差,這影響序列號(hào)和確認(rèn)號(hào)
?diff = n_len - o_len;
// 老數(shù)據(jù)在數(shù)據(jù)包中的偏移
?o_offset = o_buf - (char *)skb->data;
?/* The length of left data after o_buf+o_len in the skb data */
// 老數(shù)據(jù)左邊的第一個(gè)數(shù)據(jù)
?o_left = skb->len - (o_offset + o_len); if (diff <= 0) {
// 新長(zhǎng)度不大于老長(zhǎng)度,把原來(lái)老數(shù)據(jù)右邊的數(shù)據(jù)移過(guò)來(lái)
??memmove(o_buf + n_len, o_buf + o_len, o_left);
// 老數(shù)據(jù)部分用新數(shù)據(jù)替代
??memcpy(o_buf, n_buf, n_len);
// 減少數(shù)據(jù)包的長(zhǎng)度
??skb_trim(skb, skb->len + diff);
?} else if (diff <= skb_tailroom(skb)) {
// 新長(zhǎng)度大于老長(zhǎng)度,但skb包后面的空閑區(qū)可以容納下新數(shù)據(jù)
// 擴(kuò)展數(shù)據(jù)包長(zhǎng)
??skb_put(skb, diff);
// 移老數(shù)據(jù)右邊的數(shù)據(jù)
??memmove(o_buf + n_len, o_buf + o_len, o_left);
// 拷貝新數(shù)據(jù)
??memcpy(o_buf, n_buf, n_len);
?} else {
// 新長(zhǎng)度大于老長(zhǎng)度,但skb包后面的空閑區(qū)也容納不下新數(shù)據(jù)
// 需要重新擴(kuò)展skb大小
??if (pskb_expand_head(skb, skb_headroom(skb), diff, pri))
???return -ENOMEM;
// 擴(kuò)展數(shù)據(jù)包長(zhǎng)
??skb_put(skb, diff); // 移老數(shù)據(jù)右邊的數(shù)據(jù)
??memmove(skb->data + o_offset + n_len,
???skb->data + o_offset + o_len, o_left);
// 拷貝新數(shù)據(jù)
??memcpy(skb->data + o_offset, n_buf, n_len);
?} /* must update the iph total length here */
?iph = skb->nh.iph;
?iph->tot_len = htons(skb->len); LeaveFunction(9);
?return 0;
}
9.8 應(yīng)用實(shí)例: FTP
在IPVS中只實(shí)現(xiàn)了對(duì)FTP的處理,具體代碼在net/ipv4/ipvs/ip_vs_ftp.c中實(shí)現(xiàn). 9.8.0 FTP協(xié)議應(yīng)用結(jié)構(gòu)模板 static struct ip_vs_app ip_vs_ftp = {
?.name =??"ftp",
?.type =??IP_VS_APP_TYPE_FTP,
?.protocol =?IPPROTO_TCP,
?.module =?THIS_MODULE,
?.incs_list =?LIST_HEAD_INIT(ip_vs_ftp.incs_list),
?.init_conn =?ip_vs_ftp_init_conn,
?.done_conn =?ip_vs_ftp_done_conn,
?.bind_conn =?NULL,
?.unbind_conn =?NULL,
?.pkt_out =?ip_vs_ftp_out,
?.pkt_in =?ip_vs_ftp_in,
};
9.8.1 應(yīng)用初始化和刪除
/*
?*?ip_vs_ftp initialization
?*/
static int __init ip_vs_ftp_init(void)
{
?int i, ret;
?struct ip_vs_app *app = &ip_vs_ftp; // 登記FTP應(yīng)用模板
?ret = register_ip_vs_app(app);
?if (ret)
??return ret; // 可從模塊插入時(shí)輸入端口參數(shù)指定在哪些端口上進(jìn)行FTP應(yīng)用綁定
?for (i=0; i<IP_VS_APP_MAX_PORTS; i++) {
??if (!ports[i])
???continue;
// 新建應(yīng)用實(shí)例
??ret = register_ip_vs_app_inc(app, app->protocol, ports[i]);
??if (ret)
???break;
??IP_VS_DBG(1-debug, "%s: loaded support on port[%d] = %d\n",
???? app->name, i, ports[i]);
?} if (ret)
??unregister_ip_vs_app(app); return ret;
}
/*
?*?ip_vs_ftp finish.
?*/
static void __exit ip_vs_ftp_exit(void)
{
// 直接拆除FTP應(yīng)用協(xié)議模板
?unregister_ip_vs_app(&ip_vs_ftp);
} 9.8.2 初始化FTP連接 空函數(shù)
static int
ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp)
{
?return 0;
}
9.8.3 結(jié)束FTP連接 空函數(shù)
static int
ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp)
{
?return 0;
}
9.8.4 處理FTP進(jìn)入方向數(shù)據(jù)
進(jìn)入方向的數(shù)據(jù)是FTP客戶端發(fā)出的, 和子連接相關(guān)的命令為PORT命令,建立一個(gè)主動(dòng)模式的子連接
/*
?* Look at incoming ftp packets to catch the PASV/PORT command
?* (outside-to-inside).
?*
?* The incoming packet having the PORT command should be something like
?*????? "PORT xxx,xxx,xxx,xxx,ppp,ppp\n".
?* xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number.
?* In this case, we create a connection entry using the client address and
?* port, so that the active ftp data connection from the server can reach
?* the client.
?*/
static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
???struct sk_buff **pskb, int *diff)
{
?struct iphdr *iph;
?struct tcphdr *th;
?char *data, *data_start, *data_limit;
?char *start, *end;
?__u32 to;
?__u16 port;
?struct ip_vs_conn *n_cp; /* no diff required for incoming packets */
?*diff = 0; /* Only useful for established sessions */
// 發(fā)子連接信息數(shù)據(jù)時(shí)主連接必然是TCP連接建立好狀態(tài),否則就出錯(cuò)
?if (cp->state != IP_VS_TCP_S_ESTABLISHED)
??return 1; /* Linear packets are much easier to deal with. */
// 讓數(shù)據(jù)包可寫(xiě)
?if (!ip_vs_make_skb_writable(pskb, (*pskb)->len))
??return 0; /*
? * Detecting whether it is passive
? */
// 協(xié)議頭指針定位
?iph = (*pskb)->nh.iph;
?th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); /* Since there may be OPTIONS in the TCP packet and the HLEN is
??? the length of the header in 32-bit multiples, it is accurate
??? to calculate data address by th+HLEN*4 */
// 數(shù)據(jù)定位
?data = data_start = (char *)th + (th->doff << 2);
?data_limit = (*pskb)->tail;
// 防止數(shù)據(jù)越界
?while (data <= data_limit - 6) {
??if (strnicmp(data, "PASV\r\n", 6) == 0) {
// PASV命令,表示要進(jìn)入被動(dòng)模式
???/* Passive mode on */
???IP_VS_DBG(1-debug, "got PASV at %zd of %zd\n",
????? data - data_start,
????? data_limit - data_start);
???cp->app_data = &ip_vs_ftp_pasv;
???return 1;
??}
??data++;
?} /*
? * To support virtual FTP server, the scenerio is as follows:
? *?????? FTP client ----> Load Balancer ----> FTP server
? * First detect the port number in the application data,
? * then create a new connection entry for the coming data
? * connection.
? */
// 查找FTP數(shù)據(jù)是否是PORT命令,提取出地址端口信息及其位置
?if (ip_vs_ftp_get_addrport(data_start, data_limit,
?????? CLIENT_STRING, sizeof(CLIENT_STRING)-1,
?????? '\r', &to, &port,
?????? &start, &end) != 1)
??return 1; IP_VS_DBG(1-debug, "PORT %u.%u.%u.%u:%d detected\n",
??? NIPQUAD(to), ntohs(port)); /* Passive mode off */
?cp->app_data = NULL; /*
? * Now update or create a connection entry for it
? */
?IP_VS_DBG(1-debug, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n",
??? ip_vs_proto_name(iph->protocol),
??? NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0);
// 用找到的地址端口和服務(wù)器虛地址虛端口找連接
?n_cp = ip_vs_conn_in_get(iph->protocol,
???? to, port,
???? cp->vaddr, htons(ntohs(cp->vport)-1));
?if (!n_cp) {
// 找不到連接,這是大部分的情況
// 新建連接作為子連接
??n_cp = ip_vs_conn_new(IPPROTO_TCP,
????????? to, port,
????????? cp->vaddr, htons(ntohs(cp->vport)-1),
????????? cp->daddr, htons(ntohs(cp->dport)-1),
????????? 0,
????????? cp->dest);
??if (!n_cp)
???return 0; /* add its controller */
// 子連接和主連接相連
// 不需要修改數(shù)據(jù)內(nèi)容
??ip_vs_control_add(n_cp, cp);
?} /*
? *?Move tunnel to listen state
? */
// 將子連接狀態(tài)設(shè)置為監(jiān)聽(tīng)狀態(tài)
?ip_vs_tcp_conn_listen(n_cp);
?ip_vs_conn_put(n_cp); return 1;
}
/* net/ipv4/ipvs/ip_vs_proto_tcp.c */
/*
?*?Set LISTEN timeout. (ip_vs_conn_put will setup timer)
?*/
void ip_vs_tcp_conn_listen(struct ip_vs_conn *cp)
{
?spin_lock(&cp->lock);
// 連接狀態(tài)為監(jiān)聽(tīng)
?cp->state = IP_VS_TCP_S_LISTEN;
// 連接超時(shí)為監(jiān)聽(tīng)狀態(tài)的超時(shí)
?cp->timeout = ip_vs_protocol_tcp.timeout_table[IP_VS_TCP_S_LISTEN];
?spin_unlock(&cp->lock);
}
從FTP數(shù)據(jù)中提取IP地址和端口值
/*
?* Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started
?* with the "pattern" and terminated with the "term" character.
?* <addr,port> is in network order.
?*/
static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
????? const char *pattern, size_t plen, char term,
????? __u32 *addr, __u16 *port,
????? char **start, char **end)
{
?unsigned char p[6];
?int i = 0; if (data_limit - data < plen) {
??/* check if there is partial match */
??if (strnicmp(data, pattern, data_limit - data) == 0)
???return -1;
??else
???return 0;
?}
// 模式匹配,"PORT "或"227 "
?if (strnicmp(data, pattern, plen) != 0) {
??return 0;
?}
?*start = data + plen; for (data = *start; *data != term; data++) {
??if (data == data_limit)
???return -1;
?}
?*end = data; memset(p, 0, sizeof(p));
// 解析出6個(gè)數(shù)值
?for (data = *start; data != *end; data++) {
??if (*data >= '0' && *data <= '9') {
???p[i] = p[i]*10 + *data - '0';
??} else if (*data == ',' && i < 5) {
???i++;
??} else {
???/* unexpected character */
???return -1;
??}
?} if (i != 5)
??return -1;
// 前4個(gè)是地址
?*addr = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
// 后兩個(gè)是端口
?*port = (p[5]<<8) | p[4];
?return 1;
}
9.8.5 處理FTP發(fā)出數(shù)據(jù) 發(fā)出方向的數(shù)據(jù)是FTP服務(wù)器發(fā)出的, 和子連接相關(guān)的回應(yīng)為227類型回應(yīng),建立一個(gè)被動(dòng)模式的子連接 /*
?* Look at outgoing ftp packets to catch the response to a PASV command
?* from the server (inside-to-outside).
?* When we see one, we build a connection entry with the client address,
?* client port 0 (unknown at the moment), the server address and the
?* server port.? Mark the current connection entry as a control channel
?* of the new entry. All this work is just to make the data connection
?* can be scheduled to the right server later.
?*
?* The outgoing packet should be something like
?*?? "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)".
?* xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number.
?*/
static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
??? struct sk_buff **pskb, int *diff)
{
?struct iphdr *iph;
?struct tcphdr *th;
?char *data, *data_limit;
?char *start, *end;
?__u32 from;
?__u16 port;
?struct ip_vs_conn *n_cp;
?char buf[24];??/* xxx.xxx.xxx.xxx,ppp,ppp\000 */
?unsigned buf_len;
?int ret; *diff = 0; /* Only useful for established sessions */
// 發(fā)子連接信息數(shù)據(jù)時(shí)主連接必然是TCP連接建立好狀態(tài),否則就出錯(cuò)
?if (cp->state != IP_VS_TCP_S_ESTABLISHED)
??return 1; /* Linear packets are much easier to deal with. */
// 讓數(shù)據(jù)包可寫(xiě)
?if (!ip_vs_make_skb_writable(pskb, (*pskb)->len))
??return 0; // 子連接必須是被動(dòng)模式的
?if (cp->app_data == &ip_vs_ftp_pasv) {
// 數(shù)據(jù)定位
??iph = (*pskb)->nh.iph;
??th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
??data = (char *)th + (th->doff << 2);
??data_limit = (*pskb)->tail;
// 查找"227 "回應(yīng)中的地址端口信息
??if (ip_vs_ftp_get_addrport(data, data_limit,
??????? SERVER_STRING,
??????? sizeof(SERVER_STRING)-1, ')',
??????? &from, &port,
??????? &start, &end) != 1)
???return 1; IP_VS_DBG(1-debug, "PASV response (%u.%u.%u.%u:%d) -> "
???? "%u.%u.%u.%u:%d detected\n",
???? NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); /*
?? * Now update or create an connection entry for it
?? */
// 查找發(fā)出方向的連接
??n_cp = ip_vs_conn_out_get(iph->protocol, from, port,
?????? cp->caddr, 0);
??if (!n_cp) {
// 正常情況下是找不到的
// 新建子連接, 注意各地址端口參數(shù)的位置
???n_cp = ip_vs_conn_new(IPPROTO_TCP,
?????????? cp->caddr, 0,
?????????? cp->vaddr, port,
?????????? from, port,
?????????? IP_VS_CONN_F_NO_CPORT,
?????????? cp->dest);
???if (!n_cp)
????return 0; /* add its controller */
// 將子連接和主連接聯(lián)系起來(lái)
???ip_vs_control_add(n_cp, cp);
??} /*
?? * Replace the old passive address with the new one
?? */
// 新地址端口用連接的虛擬地址和端口
// 需要修改數(shù)據(jù)包中的數(shù)據(jù)
??from = n_cp->vaddr;
??port = n_cp->vport;
// 修改后的地址端口信息
??sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from),
???port&255, (port>>8)&255);
??buf_len = strlen(buf); /*
?? * Calculate required delta-offset to keep TCP happy
?? */
// 檢查數(shù)據(jù)長(zhǎng)度差異
??*diff = buf_len - (end-start); if (*diff == 0) {
???/* simply replace it with new passive address */
// 長(zhǎng)度相同的話直接覆蓋就行了
???memcpy(start, buf, buf_len);
???ret = 1;
??} else {
// 修改數(shù)據(jù)
???ret = !ip_vs_skb_replace(*pskb, GFP_ATOMIC, start,
?????? end-start, buf, buf_len);
??} cp->app_data = NULL;
// 連接狀態(tài)設(shè)為監(jiān)聽(tīng)
??ip_vs_tcp_conn_listen(n_cp);
// 減少連接引用計(jì)數(shù)
??ip_vs_conn_put(n_cp);
??return ret;
?}
?return 1;
}
轉(zhuǎn)載于:https://www.cnblogs.com/qq78292959/archive/2012/07/12/2587836.html
總結(jié)
以上是生活随笔為你收集整理的ip_vs实现分析(7)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 联想g50怎么安装win7系统 联想G5
- 下一篇: (转)VMware 虚拟机安装Ubunt