PACKET套接口扇出组

对于使用内存映射(MMAP)方式与应用层交互报文的AF_PACKET类型套接口,可以设置扇出组(FANOUT)。接收到的数据帧可以在组内选择任一的套接口使用,应用层可运行多个线程,处理组内不同的套接口。


扇出组创建

使用setsockopt函数创建扇出组,或者加入一个已经存在的组。如下所示,其参数option分为三个字段:fanout_flags、fanout_type和fanout_grp_id。内核在处理时,首先判断fanout_grp_id是否已经存在,不存在的话,创建一个新的扇出组,将此套接口加入到新组中。如果存在,并且全部满足以下条件,将此套接口加入到已有组中;

  • 此套接口的网络命名空间必须与组的网络命名空间相同;
  • setsockopt的fanout_flags参数必须与组的fanout_flags参数相同;
  • setsockopt的扇出类型(fanout_type参数)必须与组的类型相同;
  • 此套接口所监听的协议类型必须与组的监听类型一致;
  • 此套接口所监听的网络设备必须与组的监听设备一致。

uint32 option = (fanout_flags  (31-24) | 
                 fanout_type   (23-16) | 
                 fanout_grp_id (15-0));
setsockopt(sockfd, SOL_PACKET, PACKET_FANOUT, &option, sizeof(option));

由于fanout_grp_id为16bits,将每个网络命名空间中的组个数限制在了65536个。


扇出类型

对于扇出类型,目前内核支持以下8种类型:

1)PACKET_FANOUT_ROLLOVER
    ROLLOVER扇出类型将所接收数据帧送往同一个套接口,直到此套接口接收缓存剩余量不满四分之三时为止。之后遍历fanout扇出组内的后续套接口,找到下一个套接口,其接收缓存可用空间大于总空间的四分之一,如果找不到,继续使用之前的套接口。可用空间计算见函数__packet_rcv_has_room,对于正常套接口接收而言,avail可用空间等于sk_rcvbuf接收缓存总量减去当前使用量sk_rmem_alloc,在减去当前skb将要占用的缓存空间,即为可用空间。
    
    套接口接收缓冲区大小sk_rcvbuf可通过setsockopt函数的SO_RCVBUF选项进行自定义设置。
    
        int avail = sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc) - (skb ? skb->truesize : 0);
        if (avail > (sk->sk_rcvbuf >> ROOM_POW_OFF))
            return ROOM_NORMAL;
        else if (avail > 0)
            return ROOM_LOW;
        else
            return ROOM_NONE;

    对于mmap的套接口,以版本V1和V2为例,其可用空间计算见函数__tpacket_has_room,如果从环形buffer的head头部开始增加4分之一总长度处的buffer的状态为TP_STATUS_KERNEL,即这段空间为内核所有,认为此套接口的缓存空间正常可用。
    
        len = po->rx_ring.frame_max + 1;
        idx = po->rx_ring.head;
        if (pow_off)
            idx += len >> pow_off;
        if (idx >= len)
            idx -= len;
        return packet_lookup_frame(po, &po->rx_ring, idx, TP_STATUS_KERNEL);

    
2)PACKET_FANOUT_HASH
    哈希HASH扇出类型,内核默认的扇出类型,根据数据包的头部信息计算hash来选取套接口,使用的字段值有flow_keys_dissector_symmetric_keys定义,包括基本的网络协议号和传输层协议号,网络层地址(IPv4/IPv6),以及传输层的源和目的端口号。据此,可将同一条链接的数据流选取到同一个套接口上进行处理。 见函数fanout_demux_hash。

3)PACKET_FANOUT_LB

    负载均衡LB扇出类型目前支持round-robin算法,在组内的各个套接口见循环遍历,比较简单,见函数fanout_demux_lb。

            unsigned int val = atomic_inc_return(&f->rr_cur);   
            return val % num;

4)PACKET_FANOUT_CPU

    处理器CPU扇出类型,选取以当前处理器id值为索引的组内套接口。详函数fanout_demux_cpu。

            smp_processor_id() % num;

5)PACKET_FANOUT_RND
    
    随机RND扇出类型,使用随机算法选取组内套接口,见函数prandom_u32_max。
    
6)PACKET_FANOUT_QM
    队列映射QueueMapping扇出类型,根据当前接收的数据包skb中的queue_mapping值,选取组内的套接口,见函数fanout_demux_qm。
    
7)PACKET_FANOUT_CBPF
8)PACKET_FANOUT_EBPF

    依据经典/扩展BPF程序选取组内的套接口。

另外,ROLLOVER扇出类型可作为其它几种类型的备用类型,需要在配置其它扇出类型时,通过flags设置标志位PACKET_FANOUT_FLAG_ROLLOVER。例如设置了PACKET_FANOUT_HASH类型,但是HASH选出来的套接口的接收缓存已经不符合要求时(见以上介绍),可在通过ROLLOVER算法重新选择。此外,如果设置了PACKET_FANOUT_FLAG_DEFRAG标志位,内核扇出处理代码在选取套接口之前,将对分片数据包进行重组。

扇出组释放

扇出组目前不能通过setsockopt进行释放操作,进入组内的套接口也不能够退出组。只有当套接口关闭的时候,才能附带的从组内退出,函数fanout_release实现退出功能。在组内的最后一个套接口关闭之后,packet_release函数将释放扇出组。

    struct packet_fanout *f;

    mutex_lock(&fanout_mutex);
    if ((f = po->fanout)) {
        po->fanout = NULL;
        if (refcount_dec_and_test(&f->sk_ref))  list_del(&f->list);
        else f = NULL;
    }

 

内核版本

Linux-4.15

 

你可能感兴趣的:(网络协议)