ETF qdiscパケットを有効にした後、数秒間のみ送信されます。

ETF qdiscパケットを有効にした後、数秒間のみ送信されます。

イーサネットケーブルを介して2台のコンピュータが接続され、Ubuntu 22.04がインストールされています。コンピュータB)のサーバにUDPパケットを送信するコンピュータA)にクライアントが存在する。私はTAPRIO qdiscを上位にするETF qdiscのトラフィックに与える影響を研究しています。私はSO_PRIORITYソケットオプションとSCM_TXTIMEを利用してメッセージタイプを制御しながら、Cソケットライブラリをクライアントのソースコードとして使用しています。

ETF qdiscの効果を視覚化するために、各パケットのtxtimeを5秒間隔で対応する5秒間隔の終わりまで設定しました。したがって、サーバー側では5秒ごとにバーストが発生すると予想されます。

問題は、PFIFO qdiscから上記のETFおよびTAPRIO qdisc設定に変更した後、サーバー側でバーストが2〜3回のみ発生することです。その後、クライアントはパケットを送信しなくなります。クライアントとサーバーをアクティブに保ちながらPFIFO qdiscに戻すと、パケットはバーストなしで期待どおりに到着します。クライアントとサーバーが再びアクティブになると、TAPRIO と ETF qdisc に変更すると、わずかなバーストが発生し、その後は終了します。

この動作の原因は何ですか?

私のコードの関連部分は次のとおりです。

//setting socket options:
static void setsockopt_txtime(int fd)
{
    struct sock_txtime so_txtime_val = { .clockid = CLOCK_TAI };
    struct sock_txtime so_txtime_val_read = { 0 };
    socklen_t vallen = sizeof(so_txtime_val);
    so_txtime_val.flags = (SOF_TXTIME_REPORT_ERRORS);
    if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
        &so_txtime_val, sizeof(so_txtime_val)))
        error(1, errno, "setsockopt txtime");

    if (getsockopt(fd, SOL_SOCKET, SO_TXTIME,
        &so_txtime_val_read, &vallen))
        error(1, errno, "getsockopt txtime");

    if (vallen != sizeof(so_txtime_val) ||
        memcmp(&so_txtime_val, &so_txtime_val_read, vallen))
        error(1, 0, "getsockopt txtime: mismatch");
}
//sending message and setting txtime for message
static int l2_send(int fd, void* buf, int len, __u64 txtime, struct sockaddr_in *servaddr)
{
    char control[CMSG_SPACE(sizeof(txtime))] = {};
    struct cmsghdr* cmsg;
    struct msghdr msg;
    struct iovec iov;
    ssize_t cnt;

    iov.iov_base = buf;
    iov.iov_len = len;

    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (struct sockaddr*)servaddr;
    msg.msg_namelen = sizeof(*servaddr);

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    //We specify the transmission time in the CMSG.
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);

    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_TXTIME;
    cmsg->cmsg_len = CMSG_LEN(sizeof(__u64));
    *((__u64*)CMSG_DATA(cmsg)) = txtime;

    cnt = sendmsg(fd, &msg, 0);
    if (cnt < 1) {
        //pr_err("sendmsg failed: %m");
        printf("sending message failed!\n");
        return cnt;
    }
    printf("messaage sent!\ntxtime:%lf\n\n",(double)txtime);
    return cnt;
}
double timer_difference(struct timespec* tval_before, struct timespec* tval_after, struct timespec* tval_result)
{
    clock_gettime(CLOCK_TAI, tval_after);
    timespecsub(tval_after, tval_before, tval_result);
    double time_elapsed = (double)tval_result->tv_sec + ((double)tval_result->tv_nsec / 1000000000.0f);
    return time_elapsed;
}


//Relevant part of main funtion for calculating txtime:
int main(int argc, char* argv[]) {
.
.
    struct timespec txtime_base,txtime_base_after,txtime_difference;    
    // Creating socket file descriptor 
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }
    setsockopt_txtime(sockfd);
    memset(&servaddr, 0, sizeof(servaddr));
    // Filling server information 
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(server_port);
    inet_aton("10.0.0.70", &servaddr.sin_addr.s_addr);
    if (connect(sockfd , &servaddr,  sizeof(servaddr)))
        error(1, errno, "connect");

    clock_gettime(CLOCK_TAI, &txtime_base);
    clock_gettime(CLOCK_TAI, &txtime_base_after);
    txtime = txtime_base.tv_sec * (__u64)1000000000+ TXTIME_PERIOD*(__u64)8*(__u64)1000000000 + txtime_base.tv_nsec;
  
        while (1)
        {
            if(TXTIME_PERIOD<timer_difference(&txtime_base,&txtime_base_after,&txtime_difference)) //setting txtime here
            {
                clock_gettime(CLOCK_TAI, &txtime_base);
                txtime = txtime_base.tv_sec * (__u64)1000000000+ (__u64)TXTIME_PERIOD*(__u64)2*(__u64)1000000000 + txtime_base.tv_nsec;
                printf("in txtime if!\n");
            }
            while (time_elapsed < send_interval)  //sending interval without txtime here
            {
                
                time_elapsed = timer_difference(&tval_before, &tval_after, &tval_result);               
            } 
            txtime=txtime+(__u64)1000;       //creating 1 μs gap betwwen packets of one burst
            l2_send(sockfd, (const char*)send_buffer, strlen(send_buffer), txtime,&servaddr);
.
.

ベストアンサー1

最も可能性の高い問題は、ARPパケットにSO_TXTIMEメタデータフラグが設定されていないことです。 ETFは、ARPパケットを含むSO_TXTIMEフラグを持たないすべてのパケットを破棄します。 Linux AFAIRは30秒ごとに隣接エントリを無効にしてからそのエントリを削除するため、ARPを使用して10.0.0.70 IPのMACを確認できません。

解決策は、prioバンド! = 0でmqprioqdiscとetfリーフを使用することです。コード内のsetsockopt(..., SO_PRIORITY, ...)送信側ソケットの優先順位を!= 0に設定する必要があります。これにより、生成されたトラフィックのみがETF qdiscを通過し、他のトラフィック(ARPなど)は可能な優先順位0または発信パケットを破棄しないpfifo_fast他のqdiscを使用します。

代替ソリューション:静的隣接エントリを使用します。 Linuxが隣接テーブル(またはよりよく知られている場合はARPテーブル)でIPのMACをチェックしている場合、そのエントリは永続的です。

ip neigh add 10.0.0.70 dev eth0 lladdr 00:00:00:02:02:02その中には、00:00:00:02:02:0210.0.0.70 IPで構成されたネットワークカードのMACアドレスがあります。

おすすめ記事