2016-11-29 4 views
2

Ich möchte die Linux-Socket-Option SO_RXQ_OVFL verwenden, um UDP-Überlauf zu erkennen. Diese Option verwendet eine zusätzliche Nachricht, um die Anzahl der gelöschten Pakete zu melden. Von Mann:Wie kann man mit Hilfe von SO_RXQ_OVFL einen UDP-Überlauf auf einem Linux-Socket erkennen?

SO_RXQ_OVFL (since Linux 2.6.33) 
Indicates that an unsigned 32-bit value ancillary message (cmsg) should be  
attached to received skbs indicating the number of packets dropped by the 
socket between the last received packet and this received packet. 

Mein Code findet die zusätzliche Nachricht nicht. Hier ist, was ich tue:

Beim Start ich den Socket erstellen und die SO_RXQ_OVFL Socket-Option angeben:

int dropmonitor_on = 1; 
if (setsockopt(udpSocket, SOL_SOCKET, SO_RXQ_OVFL, &dropmonitor_on, sizeof(dropmonitor_on)) != 0) 
{ 
    perror("setsockopt SO_RXQ_OVFL not supported by your Linux Kernel"); 
} 

Dann habe ich eine Funktion erhalten, die recvmsg ruft und sucht die Neben Nachricht:

struct sockaddr_in src_addr; // The source address will be assigned to here 

struct iovec iov[1]; 
iov[0].iov_base=ap_rxBuffer; 
iov[0].iov_len=a_maxSizeBytes-1; 

int cmsg_len = CMSG_SPACE(sizeof(uint32_t)); 
char cmsg[CMSG_SPACE(sizeof(uint32_t))]; 
memset(cmsg,0,cmsg_len); 

struct msghdr message; 
memset(&message,0,sizeof(struct msghdr)); 
message.msg_name=&src_addr; 
message.msg_namelen=sizeof(struct sockaddr_in); 
message.msg_iov=iov; 
message.msg_iovlen=1; 
message.msg_control=cmsg; 
message.msg_controllen=cmsg_len; 

int receivedBytes = 0; 

if ((receivedBytes = recvmsg(a_socket, &message, 0)) == SOCKET_ERROR) 
{ 
    closeSocket(a_socket); 
    fatal("recvmsg() failed"); 
} 
else 
{ 
    // Reception successful so interrogate ancillary message to get number of dropped packets 

    int udp_packets_dropped = 0; 

    struct cmsghdr* p_cmsg; 
    p_cmsg = CMSG_FIRSTHDR(&message); 
    for (p_cmsg = CMSG_FIRSTHDR(&message); p_cmsg != NULL; p_cmsg = CMSG_NXTHDR(&message, p_cmsg)) 
    { 
     if ((p_cmsg->cmsg_level == SOL_SOCKET) && (p_cmsg->cmsg_type == SO_RXQ_OVFL)) 
     { 
      int* p_udp_packets_dropped = (int *) CMSG_DATA(p_cmsg); 
      udp_packets_dropped = *p_udp_packets_dropped; 
      cout << "UDP pkts dropped: " << udp_packets_dropped << endl; 
      break; 
     } 
    } 
    if (p_cmsg == NULL) 
    { 
     fatal("Error: p_cmsg == NULL"); 
    } 
} 

Wenn ich führen sie den Code mit diesem fatalen Fehler stoppt:

Error: p_cmsg == NULL 

die i s generiert durch den obigen Code und zeigt an, dass keine zusätzliche Nachricht gefunden wurde. Seltsamerweise bekomme ich manchmal eine ergänzende Nachricht, vielleicht habe ich etwas nicht initialisiert.

Ich habe den Code sorgfältig überprüft, kann aber nichts falsches sehen. Ich wäre Ihnen für eine Hilfe dankbar.

Antwort

1

Ich habe auch dieses Problem.

Nach einiger graben, fand ich einige Hinweise in den Kernel-Quellcode: https://github.com/torvalds/linux/blob/master/net/socket.c

static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk, 
        struct sk_buff *skb) 
{ 
    if (sock_flag(sk, SOCK_RXQ_OVFL) && skb && SOCK_SKB_CB(skb)->dropcount) 
     put_cmsg(msg, SOL_SOCKET, SO_RXQ_OVFL, 
      sizeof(__u32), &SOCK_SKB_CB(skb)->dropcount); 
} 

Blick auf die "if (... & & SOCK_SKB_CB (SKB) -> dropcount)", ich glaube, Wenn der Dropcount 0 ist, sollte das cmsg leer sein. Ihr Code sollte korrekt sein.

+1

Ich habe einige Testcodes geschrieben und jetzt kann ich sicher sein. Ich habe auch festgestellt, dass der Wert in SOCK_SKB_CB (skb) -> dropcount nach recvmsg nicht zurückgesetzt wird. Als ich den Kernel-Code gelesen habe, war ich voller Neugierde, warum ich den irgendwo den Wert nicht zurücksetzen kann. Das heißt, wenn der Wert nicht Null ist, erhalten Sie den Wert in cmsg. – alpha

0

Wenn Sie Pakete mit sendmsg mit der gleichen Struktur cmsghdr senden, die Sie für recvmsg verwendet, beachten Sie, dass im Gegensatz zu cmsghdr Steuerdaten für IP_PKTINFO, IP_TOS oder IP6_TOS, das Vorhandensein von SO_RXQ_OVFL Nachrichtendaten in der cmsghdr wird einen EINVAL Fehler verursacht wenn Sie versuchen, es zu senden.

Wenn Sie beim recvmsg-Aufruf keine anderen Optionen wie IP_PKTINFO usw. festlegen, können Sie einfach msg_controllen auf Null und msg_control auf NULL setzen. Wenn Sie von IP_PKTINFO für an INADDR_ANY gebundene Sockets abhängig sind, um sicherzustellen, dass die Quell-IP-Adresse einer Antwort der Ziel-IP-Adresse der Abfrage entspricht, müssen Sie die Bytedaten von msg_control durchlaufen und nur Cmsg-Typen kopieren, die von sendmsg unterstützt werden .

Verwandte Themen