Advertisement

suricata的Decode协议解码流程源码分析

阅读量:

《suricata中DPDK收发包源码分析1》《suricata中DPDK收发包源码分析2》已经对DPDK收包线程第一个模块 -- ReceiveDPDK模块的源码进行了分析,今天我们一起来分析一下第二个模块 -- DecodeDPDK解码模块的源码。

Decode解码的源代码调用栈如下:

复制代码
 DecodeDPDK(解码模块入口)

    
     -->DecodeLinkLayer
    
     -->DecodeEthernet(以太网层)
    
         -->DecodeNetworkLayer(网络层)
    
             -->DecodeIPV4
    
                 -->DecodeIPV4Packet
    
                 -->Defrag(IPv4分片包做重组)
    
                 -->DecodeTCP
    
                     -->DecodeTCPPacket(TCP报文解析)
    
                     -->FlowSetupPacket(设置建流标记和哈希值)
    
                 -->DecodeUDP
    
                     -->DecodeUDPPacket(UDP报文解析)
    
                     -->DecodeTeredo (Teredo隧道解码)
    
                         -->PacketTunnelPktSetup(构造一个pseudo隧道伪装报文)
    
                             -->DecodeTunnel(隧道包解码)
    
                     -->DecodeGeneve(Geneve隧道解码)
    
                         -->PacketTunnelPktSetup(构造一个pseudo隧道伪装报文)
    
                     -->DecodeVXLAN(VXLAN隧道解码)
    
                         -->PacketTunnelPktSetup(构造一个pseudo隧道伪装报文)
    
                     -->FlowSetupPacket(设置建流标记和哈希值)
    
                 -->DecodeICMPV4
    
                     -->DecodePartialIPV4(ICMP差错报文解析,不可达报文、参数错误、超时报文等等)
    
                     -->FlowSetupPacket(设置建流标记和哈希值)
    
                 -->DecodeGRE
    
                     -->PacketTunnelPktSetup(构造一个pseudo隧道伪装报文)
    
                 -->DecodeSCTP
    
                 -->DecodeESP
    
             -->DecodeIPV6
    
             -->DecodePPPOESession
    
             -->DecodePPPOEDiscovery
    
             -->DecodeVLAN
    
             -->DecodeIEEE8021ah
    
             -->ETHERNET_TYPE_ARP
    
             -->DecodeMPLS

一般报文的解码流程比较简单,先确定报文的起始位置和总长度,这两个值在ReceiveDPDKLoop中PacketSetData函数,将DPDK mbuf的报文起始地址和报文总长度赋值给了suricata报文Pakcet结构体的ext_pkt和pktlen。

复制代码
 inline int PacketSetData(Packet *p, const uint8_t *pktdata, uint32_t pktlen)

    
 {
    
     SET_PKT_LEN(p, (size_t)pktlen);
    
     if (unlikely(!pktdata)) {
    
     return -1;
    
     }
    
     // ext_pkt cannot be const (because we sometimes copy)
    
     p->ext_pkt = (uint8_t *) pktdata;
    
     p->flags |= PKT_ZERO_COPY;
    
  
    
     return 0;
    
 }

DPDK解码流程的函数入口是DecodeDPDK,解码流程按照以太网层、网络层、传输层以及负载一层层找到报文中指定的位置,并且记录相关字段,最后在FlowSetupPacket函数中根据建流的需要打上相应的标记以及计算出流表的哈希值。

复制代码
 void FlowSetupPacket(Packet *p)

    
 {
    
     p->flags |= PKT_WANTS_FLOW;  //设置 要建流的标记
    
     p->flow_hash = FlowGetHash(p); //计算流的哈希值
    
 }

需要特别说明的是解码时对隧道报文的处理,在DecodeIPV4中有对gre报文的处理, 在DecodeUDP中有针对Teredo、Geneve和VXLAN隧道报文的处理,还有IPv4 in IPv6, IPv6 in IPv4以及IPv6 in IPv6等隧道, 都会针对隧道报文调用PacketTunnelPktSetup重新生成一个新的Packet,然后调用PacketEnqueueNoLock将这个报文放入队列中,在收包线程TmThreadsSlotVarRun函数调用Decode这个slot后,通过PacketDequeueNoLock取出队列中的报文再次进行slot的调用处理。在后续代码中,涉及到pseudo报文的处理流程,都是针对隧道或者重组这种特殊构造的报文的处理。

报文Decode协议解码后,相关字段都存在Packet结构体中,具体字段含义如下:

复制代码
 typedef struct Packet_

    
 {
    
     /* Addresses, Ports and protocol
    
      * these are on top so we can use
    
      * the Packet as a hash key */
    
     Address src; //ipv4或ipv6源地址
    
     Address dst; //ipv4或ipv6目的地址
    
     union {
    
     Port sp; //源端口
    
     // icmp type and code of this packet
    
     struct {
    
         uint8_t type; //icmp type
    
         uint8_t code; //icmp code
    
     } icmp_s;
    
     };
    
     union {
    
     Port dp; //目的端口
    
     // icmp type and code of the expected counterpart (for flows)
    
     struct {
    
         uint8_t type; //icmp type
    
         uint8_t code; //icmp code
    
     } icmp_d;
    
     };
    
     uint8_t proto; //传输层协议号 tcp/udp/icmp等
    
     /* make sure we can't be attacked on when the tunneled packet
    
      * has the exact same tuple as the lower levels */
    
     uint8_t recursion_level;
    
  
    
     uint16_t vlan_id[2]; //内外层vlan
    
     uint8_t vlan_idx;    //vlan层数
    
  
    
     /* flow */
    
     uint8_t flowflags;  //流相关标记
    
     /* coccinelle: Packet:flowflags:FLOW_PKT_ */
    
  
    
     /* Pkt Flags */
    
     uint32_t flags;  //包的处理标记
    
  
    
     struct Flow_ *flow;  //流指针
    
  
    
     /* raw hash value for looking up the flow, will need to modulated to the
    
      * hash size still */
    
     uint32_t flow_hash; //流哈希值
    
  
    
     SCTime_t ts;  //包的时间戳
    
  
    
     union {
    
     /* nfq stuff */
    
 #ifdef HAVE_NFLOG
    
     NFLOGPacketVars nflog_v;
    
 #endif /* HAVE_NFLOG */
    
 #ifdef NFQ
    
     NFQPacketVars nfq_v;
    
 #endif /* NFQ */
    
 #ifdef IPFW
    
     IPFWPacketVars ipfw_v;
    
 #endif /* IPFW */
    
 #ifdef AF_PACKET
    
     AFPPacketVars afp_v;
    
 #endif
    
 #ifdef HAVE_NETMAP
    
     NetmapPacketVars netmap_v;
    
 #endif
    
 #ifdef HAVE_PFRING
    
 #ifdef HAVE_PF_RING_FLOW_OFFLOAD
    
     PfringPacketVars pfring_v;
    
 #endif
    
 #endif
    
 #ifdef WINDIVERT
    
     WinDivertPacketVars windivert_v;
    
 #endif /* WINDIVERT */
    
 #ifdef HAVE_DPDK
    
     DPDKPacketVars dpdk_v;  //DPDK的包变量,用于报文mbuf发送
    
 #endif
    
 #ifdef HAVE_NAPATECH
    
     NapatechPacketVars ntpv;
    
 #endif
    
 #ifdef HAVE_AF_XDP
    
     AFXDPPacketVars afxdp_v;
    
 #endif
    
     /* A chunk of memory that a plugin can use for its packet vars. */
    
     uint8_t plugin_v[PLUGIN_VAR_SIZE];
    
  
    
     /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
    
     PcapPacketVars pcap_v;
    
     };
    
  
    
     /** The release function for packet structure and data */
    
     void (*ReleasePacket)(struct Packet_ *); //报文释放或发送的函数指针
    
     /** The function triggering bypass the flow in the capture method.
    
      * Return 1 for success and 0 on error */
    
     int (*BypassPacketsFlow)(struct Packet_ *);
    
  
    
     /* pkt vars */
    
     PktVar *pktvar;
    
  
    
     /* header pointers */
    
     EthernetHdr *ethh; //以太网头指针
    
  
    
     /* Checksum for IP packets. */
    
     int32_t level3_comp_csum;
    
     /* Check sum for TCP, UDP or ICMP packets */
    
     int32_t level4_comp_csum;
    
  
    
     IPV4Hdr *ip4h; //ip头指针
    
  
    
     IPV6Hdr *ip6h; //ipv6头指针
    
  
    
     /* IPv4 and IPv6 are mutually exclusive */
    
     union {
    
     IPV4Vars ip4vars; //ipv4 options
    
     struct {
    
         IPV6Vars ip6vars;
    
         IPV6ExtHdrs ip6eh;  //ipv6扩展头
    
     };
    
     };
    
     /* Can only be one of TCP, UDP, ICMP at any given time */
    
     union {
    
     TCPVars tcpvars;  //tcp options
    
     ICMPV4Vars icmpv4vars; //icmp 差错报文字段(不可达等)
    
     ICMPV6Vars icmpv6vars; //icmpv6 差错报文字段
    
     } l4vars;
    
 #define tcpvars     l4vars.tcpvars
    
 #define icmpv4vars  l4vars.icmpv4vars
    
 #define icmpv6vars  l4vars.icmpv6vars
    
  
    
     TCPHdr *tcph;  //tcp头指针
    
  
    
     UDPHdr *udph;  //udp头指针
    
  
    
     SCTPHdr *sctph; //sctp头指针
    
  
    
     ESPHdr *esph;   //esp头指针
    
  
    
     ICMPV4Hdr *icmpv4h; //icmpv4头指针
    
  
    
     ICMPV6Hdr *icmpv6h; //icmpv6头指针
    
  
    
     PPPHdr *ppph; //ppp头指针
    
     PPPOESessionHdr *pppoesh; //pppoe会话头指针
    
     PPPOEDiscoveryHdr *pppoedh;//pppoe 链路发现头指针
    
  
    
     GREHdr *greh; //gre头指针
    
  
    
     /* ptr to the payload of the packet
    
      * with it's length. */
    
     uint8_t *payload;  //报文负载位置指针(传输层之后)
    
     uint16_t payload_len; //报文负载长度
    
  
    
     /* IPS action to take */
    
     uint8_t action;
    
  
    
     uint8_t pkt_src;
    
  
    
     /* storage: set to pointer to heap and extended via allocation if necessary */
    
     uint32_t pktlen; //报文总长度
    
     uint8_t *ext_pkt; //报文起始指针
    
  
    
     /* Incoming interface */
    
     struct LiveDevice_ *livedev;  //进网口信息
    
  
    
     PacketAlerts alerts; //告警信息
    
  
    
     struct Host_ *host_src;
    
     struct Host_ *host_dst;
    
  
    
     /** packet number in the pcap file, matches wireshark */
    
     uint64_t pcap_cnt;
    
  
    
  
    
     /* engine events */
    
     PacketEngineEvents events;
    
  
    
     AppLayerDecoderEvents *app_layer_events;
    
  
    
     /* double linked list ptrs */
    
     struct Packet_ *next;
    
     struct Packet_ *prev;
    
  
    
     /** data linktype in host order */
    
     int datalink;
    
  
    
     /* count decoded layers of packet : too many layers
    
      * cause issues with performance and stability (stack exhaustion)
    
      */
    
     uint8_t nb_decoded_layers;
    
  
    
     /* enum PacketDropReason::PKT_DROP_REASON_* as uint8_t for compactness */
    
     uint8_t drop_reason;
    
  
    
     /* tunnel/encapsulation handling */
    
     struct Packet_ *root; /* in case of tunnel this is a ptr
    
                        * to the 'real' packet, the one we
    
                        * need to set the verdict on --
    
                        * It should always point to the lowest
    
                        * packet in a encapsulated packet */
    
  
    
     /* ready to set verdict counter, only set in root */
    
     uint16_t tunnel_rtv_cnt;
    
     /* tunnel packet ref count */
    
     uint16_t tunnel_tpr_cnt;
    
  
    
     /** tenant id for this packet, if any. If 0 then no tenant was assigned. */
    
     uint32_t tenant_id;
    
  
    
     /* The Packet pool from which this packet was allocated. Used when returning
    
      * the packet to its owner's stack. If NULL, then allocated with malloc.
    
      */
    
     struct PktPool_ *pool;
    
  
    
 #ifdef PROFILING
    
     PktProfiling *profile;
    
 #endif
    
     /* things in the packet that live beyond a reinit */
    
     struct {
    
     /** lock to protect access to:
    
      *  - tunnel_rtv_cnt
    
      *  - tunnel_tpr_cnt
    
      *  - nfq_v.mark
    
      *  - flags
    
      */
    
     SCSpinlock tunnel_lock;
    
     } persistent;
    
 } Packet;

好了,suricata协议解码的源码分析就到这里了。喜欢文章内容的朋友,记得加个关注哦~~

全部评论 (0)

还没有任何评论哟~