当前位置: 首页 > Linux

UDP

时间:2023-04-06 22:00:29 Linux

用于连接跟踪的udp是一个非常简单的协议,连接跟踪处理起来非常容易。UDP连接跟踪控制块conststructnf_conntrack_l4protonf_conntrack_l4proto_udp4={.l3proto=PF_INET,.l4proto=IPPROTO_UDP,.allow_clash=true,.pkt_to_tuple=udp_pkt_to_tuple,.invert_tuple=udp_invert_tuple,.packet=udp_packet,.get_timeouts=udp_get_timeouts,.new=udp_new,.error=udp_error,#ifIS_ENABLED(CONFIG_NF_CT_NETLINK).tuple_to_nlattr=nf_ct_port_tuple_to_nlattr,.nlattr_to_tuple=nf_ct_port_nlattr_to_tuple,.nlattr_tuple_size=nf_ct_port_nlattr_tuple_size,.nla_policy=nf_ct_port_nla_policy,#endif#ifIS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT).ctnl_timeout={.nlattr_to_obj=udp_timeout_nlattr_to_obj,.obj_to_nlattr=udp_timeout_obj_to_nlattr,.nlattr_max=CTA_TIMEOUT_UDP_MAX,.obj_size=sizeof(unsignedint)*CTA_TIMEOUT_UDP_MAX,.nla_policy=udp_timeout_nla_policy,},#endif/*CONFIG_NF_CT_NETLINK_TIMEOUT*/.init_net=udp_init_net,.get_net_proto=udp_get_net_proto,};udp_error最先执行的是udp_error,执行错误检查staticintudp_error(structnet*net,structnf_conn*tmpl,structsk_buff*skb,unsignedintdataoff,u_int8_tpf,unsignedinthooknum){unsignedintudplen=skb->len-dataoff;conststructudphdr*hdr;结构udphdr_hdr;/*标题太小?udp头的长度够吗?*/hdr=skb_header_pointer(skb,dataoff,sizeof(_hdr),&_hdr);if(hdr==NULL){udp_error_log(skb,net,pf,"短数据包");返回-NF_ACCEPT;}/*截断/格式错误的数据包数据包长度有效性检查*/if(ntohs(hdr->len)>udplen||ntohs(hdr->len)check)returnNF_ACCEPT;/*校验和无效?忽略。*我们跳过检查传出路径上的数据包*因为假定校验和是正确的。*整我:源路由IP选项包--RR*/if(net->ct.sysctl_checksum&&hooknum==NF_INET_PRE_ROUTING&&nf_checksum(skb,hooknum,dataoff,IPPROTO_UDP,pf)){udp_error_log(skb,net,pf,"badchecksum");返回-NF_ACCEPT;}returnNF_ACCEPT;}udp_pkt_to_tuple提取传输层元组内容_hdr;/*实际上只需要前4个字节来获取端口。*/hp=skb_header_pointer(skb,dataoff,4,&_hdr);如果(hp==NULL)返回false;tuple->src.u.udp.port=hp->source;tuple->dst.u.udp.port=hp->dest;returntrue;}udp_invert_tuple根据当前报文的元组信息得到传输层的逆元组信息。简单的源到目标端口交换。staticbooludp_invert_tuple(structnf_conntrack_tuple*tuple,conststructnf_conntrack_tuple*orig){元组->src.u.udp.port=orig->dst.u.udp.port;tuple->dst.u.udp.port=orig->src.u.udp.port;returntrue;}udp_new请求方向先包校验,因为udp很简单,直接returntrue。/*当找到此协议的新连接时调用。*/staticbooludp_new(structnf_conn*ct,conststructsk_buff*skb,unsignedintdataoff,unsignedint*timeouts){returntrue;}udp_packet后续包校验,udp很简单,这里只更新超时时间和执行数据包统计更新。/*返回数据包的判断,并且可以修改conntracktype*/staticintudp_packet(structnf_conn*ct,conststructsk_buff*skb,unsignedintdataoff,enumip_conntrack_infoctinfo,unsignedint*timeouts){/*如果我们已经看到流量两种方式,这是某种UDP流。延长超时时间。*刷新连接超时,数据包统计数据已更新*//*此外,更可能是重要的,而不是探测*//*一旦遇到双向消息,设置ASURED标志,表明它不是探测消息**发送ASSURD事件*/if(!test_and_set_bit(IPS_ASSURED_BIT,&ct->status))nf_conntrack_event_cache(IPCT_ASSURED,ct);}else{nf_ct_refresh_acct(ct,ctinfo,skb,timeouts[UDP_CT_UNREPLIED]);}returnNF_ACCEPT;}udp_get_timeouts获取超时staticconstunsignedintudp_timeouts[UDP_CT_MAX]={[UDP_CT_UNREPLIED]=30*HZ,[UDP_CT_REPLIED]=180*H??Z,};structnf_udp_net{structnf_proto_netpn;unsignedinttimeouts[UDP_CT_MAX];};staticint*outsigned_ud(structnet*net){returnudp_pernet(net)->timeouts;}UDPnat控制块conststructnf_nat_l4protonf_nat_l4proto_udp={.l4proto=IPPROTO_UDP,.manip_pkt=udp_manip_pktin_range=nf_nat_l4proto_in_range,.unique_tuple=udp_unique_tuple,#ifIS_ENABLED(CONFIG_NF_CT_NETLINK).nlattr_to_range=nf_nat_l4proto_nlattr_to_range,#endif};nf_nat_l4proto_in_range是传输层的一个通用函数,用于判断源端口或目的端口是否在指定范围内。boolnf_nat_l4proto_in_range(conststructnf_conntrack_tuple*tuple,enumnf_nat_manip_typemaniptype,constunionnf_conntrack_man_proto*min,constunionnf_conntrack_man_proto*max){__be16端口;如果(maniptype==NF_NAT_MANIP_SRC)port=tuple->src.u.all;elseport=tuple->dst.u.all;返回ntohs(端口)>=ntohs(最小->所有)&&ntohs(端口)<=ntohs(最大->所有);}EXPORT_SYMBOL_GPL(nf_nat_l4proto_in_range);udp_unique_tuple从指定范围端口获取元组,使元组唯一。/*如果不指定range,则目标端口在DNAT时不能改变,源端口在SNAT时可以改变。600-1024,1024以上的映射范围为1024以上,如果指定了端口范围,则按照指定的范围。如果是NF_NAT_RANGE_PROTO_RANDOM模式,调用L3的secure_port,根据源目的IP和需要修改的端口计算一??个hash值。如果是NF_NAT_RANGE_PROTO_RANDOM_FULLY模式,直接根据得到的值计算随机数,根据范围取余,把最小值加到得到的端口上,然后判断是否被使用,然后加1判断如果它被使用。*/voidnf_nat_l4proto_unique_tuple(conststructnf_nat_l3proto*l3proto,structnf_conntrack_tuple*tuple,conststructnf_nat_range*range,enumnf_nat_manip_typemaniptype,conststructnf_conn*ct,u16*rover){unsignedintrange_size,min,max,i;__be16*端口指针;u_int16_t关闭;if(maniptype==NF_NAT_MANIP_SRC)portptr=&tuple->src.u.all;elseportptr=&tuple->dst.u.all;/*如果没有指定范围...判断是否指定了特定端口范围*/if(!(range->flags&NF_NAT_RANGE_PROTO_SPECIFIED)){/*如果没有指定特定端口范围*//*如果是dstrewrite,无法更改端口目标nat不会更改端口*/if(maniptype==NF_NAT_MANIP_DST)return;/*源端口是保留端口,所以需要保证NAT后面的源端口也是保留端口*/if(ntohs(*portptr)<1024){/*松散约定:>>512是凭证传递*//*源端口小于512,则在1-511之间选择*/如果(ntohs(*portptr)<512){min=1;范围大小=511-最小值+1;}else{/*如果大于512,则在600和1024之间选择*/min=600;范围大小=1023-最小值+1;}}else{//非保留端口可以在1024和65536之间选择min=1024;范围大小=65535-1024+1;}}else{//指定具体的端口范围min=ntohs(range->min_proto.all);max=ntohs(range->max_proto.all);如果(不太可能(最大值<最小值))交换(最大值,最小值);range_size=max-min+1;}if(range->flags&NF_NAT_RANGE_PROTO_RANDOM){off=l3proto->secure_port(tuple,maniptype==NF_NAT_MANIP_SRC?tuple->dst.u.all:tuple->src.u.all);}elseif(range->flags&NF_NAT_RANGE_PROTO_RANDOM_FULLY){off=prandom_u32();}else{off=*漫游者;}对于(我=0;;++off){*portptr=htons(min+off%range_size);/*端口已经被使用,则加1尝试,直到满足要求或者应该遍历所有情况*/if(++i!=range_size&&nf_nat_used_tuple(tuple,ct))continue;/*如果没有设置random,则设置当前选择的端口号*/if(!(range->flags&NF_NAT_RANGE_PROTO_RANDOM_ALL))*rover=off;返回;}}udp_manip_pkt在传输层进行Nat转换staticbooludp_manip_pkt(structsk_buff*skb,conststructnf_nat_l3proto*l3proto,unsignedintiphdroff,unsignedinthdroff,conststructnf_conntrack_tuple*tuple,enumnf_nat_manip_typemaniptype){structudphdr*hdr;布尔do_csum;如果(!skb_make_writable(skb,hdroff+sizeof(*hdr)))返回false;hdr=(structudphdr*)(skb->data+hdroff);do_csum=hdr->检查||skb->ip_summed==CHECKSUM_PARTIAL;__udp_manip_pkt(skb,l3proto,iphdroff,hdr,tuple,maniptype,do_csum);returntrue;}staticvoid__udp_manip_pkt(structsk_buff*skb,conststructnf_nat_l3proto*l3proto,unsignedintiphdroff,structudphdr*hdr,conststructnf_conntrack_tuple*tuple,enumnf_nat_manip_typemaniptype,booldo_csum){__be16*portptr,newport;if(maniptype==NF_NAT_MANIP_SRC){/*去掉src端口*/newport=tuple->src.u.udp.port;portptr=&hdr->source;}else{/*去掉dst端口*/newport=tuple->dst.u.udp.port;portptr=&hdr->dest;}if(do_csum){l3proto->csum_update(skb,iphdroff,&hdr->check,tuple,maniptype);inet_proto_csum_replace2(&hdr->check,skb,*portptr,newport,false);如果(!hdr->check)hdr->check=CSUM_MANGLED_0;}*portptr=newport;}