DPVS的数据流量从以流入流出划分为:inbondoutbond;outbond:指数据包从内网服务器DPVS外网服务器(如qq。com)inbond:指数据包从外网服务器(如qq。com)DPVS内网服务器outbond方向数据包处理流程 内网服务器需要访问外网某个功能接口,请求数据包到达了DPVS服务器。数据包从网卡某个队列queuex进入后,被cpux接收并开始相关的逻辑处理。收包路径大概包括如下:netiflooplcorejobrecvfwdlcoreprocesspacketsnetifdelivermbufnetifdelivermbuf函数中根据packettype分别调用相应的func函数,func类型主要包括:ipv4、ipv6、arp三种类型。这里主要以IPv4分析SNAT整个过程。IPv4类型的包处理函数是ipv4rcv,经过IP层相关检查和校验处理后,调用INETHOOK来执行相关的钩子函数,这里调用的是INETHOOKPREROUTING位置钩子,包括:dpvspreroutingdpvsin函数,核心代码入口在dpvsin里边 在dpvsin中其实调用的是dpvsin函数,主逻辑入口在这个函数中体现:首先调用协议查找函数dpvsprotolookup,确认此数据包是哪种协议类型,如TCP或UDP等,每种协议的相关处理逻辑不同,后续的相关操作会根据对应协议注册的回调函数来执行。假设本次是请求是首包,需要调用proto的connsched函数进行连接的初始化和调度,TCP的连接调度函数是tcpconnsched。调度时,只有TCPsyn包才能触发正常调度流程。首先,根据数据包的相关数据确认该包是否属于我们配置的服务,调用dpvsservicelookup函数进行查找,不过这里查找到的service类型是match方式。如果找不到响应的svc就跳出SNAT主流程。找到svc后,调用dpvsschedule进行主调度流程,并创建初始化新的连接Entry。调用svcschedulerschedule函数,从RS列表中挑选出一个RS作为dest对于SNAT模式,调用dpvssnatschedule()函数此时已确认dest作为源IP地址,调用safetch根据源地址选择相对应的源port调用dpvsconnfillparam将相关数据赋值到param变量中去。确定相关参数后,调用dpvsconnnew建立初始化新的连接条目。申请newconn连接内存资源,调用dpvsconnalloc初始化inbond和outbond连接表关联的ntuple哈希初始化newconn的相关字段为conn绑定对应的dest,并设置对应的xmit函数添加newconn到连接表中dpvsconnhash初始化相关的计数器和定时器到此为止,一个新的SNAT连接已经完成连接调度成功后,根据模式,SNAT初始化方向为DPVSCONNDIROUTBOUND不同于其他模式。调用proto的statetrans函数,进行相关协议状态机的转换。调用xmitoutbound将数据包发送出去。调用相关接口,确认output路由,并修改三层和四层信息确认下一跳等相关信息,调用neighoutput发出数据包 到此从SNAT数据包的接收、处理、转发等过程,完成SNAToutbond方向的整个流程。inbond方向数据包处理流程 内网服务器发送请求数据包后,外网服务器执行相关操作,发出响应数据包,此数据包到达了DPVS服务器。此时DPDK驱动程序会根据关键字段进行FDIR规则匹配,数据包从同样的网卡度列queuex进入后,被cpux接收并开始相关的逻辑处理,这样整个连接都是被同一个cpux来处理的。收包路径大概包括如下: 在dpvsin前的收包处理流程和outbond基本一致,这里就不分析了。根据数据包字段,确认协议类型,调用dpvsprotolookup确认本次是TCP协议根据响应数据包的4元组来查找连接表(根据元组字段和tuplehash字段),正常情况能够找到对应的连接表。进行协议的状态机转换调用xmitinbound将数据包发送出去,具体细节与outbond很类似。 不断的重复outbond和inbond流程,就能够通过多次数据包的交互完成连接过程中业务的传输,从而实现SNAT功能 存在问题:ICMP处理问题。内网服务器ping外网是一个很常见的需求,从实验测试和代码分析来看,响应ICMP包可能命中到其它网卡队列,导致连接表miss的问题,这个问题可以通过FDIR功能将ICMP包都定位到固定的网卡queue上,这样可以简单的方式来解决这个问题。 NAT模式原理 对于inbound方向的流量,实际上做的是dnat,将目标ip由lbvip转换成真正的rsip,此时后端rs是能拿到clientip的。outbond的流量做snat,将源地址换成lbvip returnverdictINETXXXaffrommbufl3type?No!Thefieldisrewrittenbynetifandconflictswithm。packettype(anunion),sousingawrappertogetaf。staticintdpvsin(voidpriv,structrtembufmbuf,conststructinethookstatestate,intaf){structdpvsiphdriph;structdpvsprotoprot;structdpvsconnconn;intdir,verdict,err,related;booldropfalse;lcoreidtcid,peercid;ethtypetetypembufpackettype;FIXME:useotherfield?assert(mbufstate);获取当前运行的lcoreidcidpeercidrtelcoreid();数据包不是发往本机的,直接返回ACCEPT,之后执行ipv4rcvfinif(unlikely(etype!ETHPKTHOST))returnINETACCEPT;填充内部dpvsiphdr,如果出错,主要是协议族不正确,直接返回ACCEPTif(dpvsfilliphdr(af,mbuf,iph)!EDPVSOK)returnINETACCEPT;处理ICMP消息,类似于linux内核中icmperror相关的处理逻辑if(unlikely(iph。protoIPPROTOICMPiph。protoIPPROTOICMPV6)){handlerelatedICMPerrortoexistingconnverdictdpvsinicmp(af,mbuf,related);if(relatedverdict!INETACCEPT)returnverdict;letunrelatedandvalidICMPgoesdown,mayimplementICMPfwdinthefuther。}查找四层处理协议,目前实现了tcp,udp和icmpprotdpvsprotolookup(iph。proto);if(unlikely(!prot))returnINETACCEPT;DefragipvsforwardingTCPUDPisnotsupportedforsomereasons,RSSflowdirectordonotsupportTCPUDPfragments,meansitsnotabletodirectfragstosamelcoreasoriginalTCPUDPpackets。perlcoreconntablewillmissiffragsreachswronglcore。Ifweredirectfragstocorrectlcore,itmaycauseperformanceissue。AlsoitneedtounderstandRSSalgorithm。Moreover,forthecasefragsinsameflowarenotoccurinsamelcore,agloballockisneeded,whichisnotagoodidea。目前不支持ip分片,此处与flowdirector相关if(afAFINETip4isfrag(ip4hdr(mbuf))){RTELOG(DEBUG,IPVS,s:fragnotsupport。,func);returnINETDROP;}调用proto相关connlookup函数查找会话,tcp中为tcpconnlookup。有可能会drop掉。dir是设置数据流方向,从client到LB,还是从realserver到LB,peercid为查找时决定处理该连接的lcoreidpacketbelongstoexistingconnection?connprotconnlookup(prot,iph,mbuf,dir,false,drop,peercid);if(unlikely(drop)){RTELOG(DEBUG,IPVS,s:denyiptrytovisit。,func);returnINETDROP;}Theconnectionisnotlocallyfound,howevertheredirectisfoundsoforwardthepackettotheremoteredirectownercore。如果不在当前lcore上处理,则恢复mbufdataoff指向L2header后转发给其他lcore处理,此处ringenqueue成功后返回INETSTOLEN,否则返回DROP丢弃数据包if(cid!peercid){recovermbuf。dataofftoouterEtherheaderrtepktmbufprepend(mbuf,(uint16t)sizeof(structrteetherhdr));returndpvsredirectpkt(mbuf,peercid);}对于新建的连接,肯定是没有会话的,connsched根据请求选择一个后端realserver建立连接if(unlikely(!conn)){tryscheduleRSandcreatenewconnection调用proto中connsched接口选择一个后端rs建立连接,如果创建连接失败,返回verdictif(protconnsched(prot,iph,mbuf,conn,verdict)!EDPVSOK){RTELOG(DEBUG,IPVS,s:failtoschedule。,func);returnverdict;}snat模式,则是内部服务器访问外部服务,内网服务器dpvs外网服务器(baidu),所以设置dirDPVSCONNDIROUTBOUNDonlySNATtriggersconnectionbyinsideoutsidetraffic。if(conndestfwdmodeDPVSFWDMODESNAT)dirDPVSCONNDIROUTBOUND;else其余模式设置dirDPVSCONNDIRINBOUNDdirDPVSCONNDIRINBOUND;}else{assert(conndestsvc!NULL);if(conndestconndestsvcprotconnexpirequiescent(conndestsvcflagsDPVSCONNFEXPIREQUIESCENT)){if(rteatomic16read(conndestweight)0){RTELOG(INFO,IPVS,s:theconnisquiescent,expireitrightnow,anddropthepacket!,func);protconnexpirequiescent(conn);dpvsconnput(conn);returnINETDROP;}}}特殊处理,synproxyif(connflagsDPVSCONNFSYNPROXY){if(dirDPVSCONNDIRINBOUND){FilteroutinackpacketwhencpisatSYNSENTstate。Dropitifnotavalidpacket,storeitotherwiseif(0dpvssynproxyfilterack(mbuf,conn,prot,iph,verdict)){dpvsstatsin(conn,mbuf);dpvsconnput(conn);returnverdict;}Reusesynproxysessions。Reusemeansupdatesynproxyseqstructandcleanackmbufetc。if(0!dpvssynproxyctrlconnreuse){if(0dpvssynproxyreuseconn(af,mbuf,conn,prot,iph,verdict)){dpvsstatsin(conn,mbuf);dpvsconnput(conn);returnverdict;}}}else{Synproxy3logic:receivesynackfromrsif(dpvssynproxysynackrcv(mbuf,conn,prot,iph。len,verdict)0){dpvsstatsout(conn,mbuf);dpvsconnput(conn);returnverdict;}}}状态迁移,tcp中为tcpstatetransif(protstatetrans){errprotstatetrans(prot,conn,mbuf,dir);if(err!EDPVSOK)RTELOG(WARNING,IPVS,s:failtotransstate。,func);}connoldstateconnstate;holdingtheconn,needaputlater。根据流量方向dir,来选择如何发送数据if(dirDPVSCONNDIRINBOUND)returnxmitinbound(mbuf,prot,conn);elsereturnxmitoutbound(mbuf,prot,conn);} 原文链接:https:www。cnblogs。comcodestackp15717792。html