Forum Linux.redhat [RESEAU] Fragmentations de paquets tcp

Posté par  . Licence CC By‑SA.
Étiquettes :
6
8
oct.
2013

Salut à tous,
ça concerne un problème réseau, j'suis une bille en réseau alors soyez indulgent si je dis de grosses conneries :)

J'ai un besoin très précis, j'utilise une application serveur qui envoie des paquets tcp a un client.
La machine serveur est un HP Proliant DL360 G6 sous RHEL 5U4 32 bits.
J'ai 2 cartes broadcom netxtreme II BCM 5709 en failover bonding mode1 (driver bnx2) et j'ai désactivé ipv6 dans le driver.
Mon appli là dessus envoie un paquet tcp d'exactement 1044 octets sur un client.
Ce client a dans sa configuration TCP une fenêtre d'exactement 1044 octets.
J'ai comme nécessité que le paquet arrive sur mon client en un seul "morceau" sans fragmentation, or quand je regarde un peu ce qui se passe avec Wireshark je vois que le paquet de 1044 octets est fragmenté en sortie d'interface de mon serveur en 2 paquets de 512 octets.
Ma seule alternative pour que le paquet de 1044 octets passe d'un bloc, est d'augmenter la fenêtre TCP a 2088 octets ou plus (soit 2X la taille de mon paquet).
Cette application fonctionne aussi sous un serveur sous Solaris7 et on n'a pas du tout le même comportement, le paquet est transmis entier.

Ce que j'ai tenté pour le moment:
-Jouer avec sysctl sur les paramètres tcp du server (MTU,MSS,probing, …)
-Jouer avec iptable en postrouting pour forcer le MSS en sortie
-Desactiver tous les paramètres d'offload des cartes réseaux via ethtool
-Changer le protocole de congestion tcp de BIC a CUBIC

J'ai constaté que ce n'était pas un problème applicatif, j'ai fait un test en démarrant apache sur le serveur et en téléchargeant un fichier en http via un client linux du LAN sur lequel j'avais positionné le réglage suivant:
net.ipv4.tcp_rmem = 512 1392 4092
net.ipv4.tcp_window_scaling = 0

Le paramètre de tcp_rmem fixé a 1392 me permet d'avoir une fenêtre tcp de 1044 octets sur le client (pourquoi? pour le moment j'en sais rien!).
Et là c'est le drame je vois via Wireshark que là aussi j'ai des segments de 522 octets.

Le support Redhat sèche pour me donner une explication sur ce comportement.
J'ai l'impression que le systeme se reserve de la place et refuse d'utiliser toute la fenetre TCP disponible pour les données.
En même temps c'est vrai que c'est un peu un besoin à la con, vu la taille ridicule des fenêtres TCP par rapport à ce qui est en place par défaut, mais je ne peux absolument pas jouer sur ces paramètres.
Avez vous déjà été confronté à ça?

  • # packet fragementation = quand il y a des intermediaires

    Posté par  . Évalué à 4.

    d'apres wikipedia http://en.wikipedia.org/wiki/IP_fragmentation

    la fragmentation de paquet intervient quand les fenetres MTU ne sont pas les memes sur le parcours du paquet.
    ex : serveurA->switch->routeur->internet->routeur->switch->machineB
    mais c'est aussi le cas avec serveurA->switch->machineB

    il me semble que sur les switchs y a des options pour eviter la fragmentation.

    de là il faut aussi relire la definition de la fenetre MTU 1500
    c'est bien 1500 moins les entetes, quelques CRC, etc
    si ca se trouve il ne reste que 1040 pour ton paquet de 1044 => trop gros passera pas => on fragmente

    eventuellement la RFC recommande de jouer avec le MSS pour que les machines se mettent d'accord sur les tailles transmissibles.

    • [^] # Re: packet fragementation = quand il y a des intermediaires

      Posté par  . Évalué à 1.

      J'ai un MTU de 1500 sur tout le trajet et rien au niveau des switchs qui pourraient indiquer de fragmenter.
      Je peux envoyer des paquets > 1044 octets sans problème si la fenêtre TCP est au moins équal au double de la taille des paquets, donc le problème ne vient pas de là, et j'ai aussi le même souci lors des tests en HTTP avec une machine cliente qui ne passe pas les mêmes équipements réseaux.
      Effectivement les MSS ne sont pas les mêmes lors de la négociation SYN/SYN-ACK, j'ai 1380 pour mon client et 1460 pour mon serveur.
      J'ai forcé avec iptable pour avoir un MSS a 1380 coté serveur, et j'ai bien 1380 des 2 cotés mais la fragmentation est toujours là.
      C'est vraiment la taille des fenêtres coté client qui posent problème.

  • # oui mais..

    Posté par  . Évalué à 2.

    J'ai comme nécessité que le paquet arrive sur mon client en un seul "morceau" sans fragmentation

    AMHA, en TCP, tu ne devrais pas avoir besoin de cette nécessité : le client doit continuer à lire sur son socket tant qu'il n'a pas reçu ce qui lui est nécessaire. D'autant plus que même si le paquet arrive en un seul morceau au niveau du client il n'y a pas, j'en suis quasi certain, de garantie que l'OS sous-jacent à l'application lui fournira le dit paquet de donnée en un seul appel de lecture (read) sur le socket.

    • [^] # Re: oui mais..

      Posté par  . Évalué à 1.

      L'appli a été conçu comme ça:
      On attend un segment de data TCP de 1044 octets donc on positionne une fenêtre d'exactement cette taille là.
      Ce comportement fonctionne a merveille avec une vielle Sun Enterprise 250 sous Solaris7 comme serveur.
      Sous linux ça ne fonctionne pas pareil, l'émetteur ne veut rien entendre , tant que la fenêtre TCP n'est pas égal à 2X la taille du segment de data, il fragmente le paquet en deux.

      J'ai lu ça dans man 7 socket:
      SO_SNDBUF
      Sets or gets the maximum socket send buffer in bytes. The kernel doubles this value (to allow space for bookkeeping overhead) when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). The default value is set by the /proc/sys/net/core/wmem_default file and the maximum allowed value is set by the /proc/sys/net/core/wmem_max file. The minimum (doubled) value for this option is 2048.

      RedHat me dit que ça n'a pas d'impact sur la fragmentation mais d'après tous les tests que j'ai effectué, quelque soit la taille du paquet que je veux transmettre, le serveur envoie toujours des segments de data de la moitié de la taille de la fenêtre TCP cliente.

      • [^] # Re: oui mais..

        Posté par  . Évalué à 3.

        Oui, la taille est spécifiée à l'établissement de la connexion TCP : fais une capture tcpdump et regarde le MSS (maximum segment size). C'est fonction du MTU et de la receive window.

        Mais tu ne pourras pas forcer la taille d'un paquet TCP : ça n'a même aucun sens puisque c'est un protocole stream.

        Dans ton cas, tu peux essayer l'option TCP_CORK.

    • [^] # Re: oui mais..

      Posté par  . Évalué à 1.

      Plus d'info sur mon besoin:
      Tu as parfaitement raison de dire que le client doit continuer à lire sur son socket tant qu'il n'a pas reçu ce qui lui est nécessaire et que finalement la fragmentation n'a pas d'impact puisqu'il réassemblera.
      Néanmoins, il y'a une "sécurité" qui écoute ce qui sort en sortie de mon serveur.
      Si le segment de data émis ne fait pas exactement 1044 octets alors on considère qu'il y'a un problème.
      Je sais c'est vraiment un besoin merdique mais je n'ai aucun pouvoir pour changer cette façon de procéder.

      Je vais me replonger vers les algorithmes de congestion, il faudrait que je puisse trouver celui utilisé sous solaris 7 par défaut.

      Merci de votre aide.

  • # trucs & machins...

    Posté par  (Mastodon) . Évalué à 3. Dernière modification le 01 novembre 2013 à 23:09.

    Salut JuD Jean-Clume,

    Je dirais qu'il faut faire fermer sa cacahouète à la carte réseau (à toutes les cartes réseaux, puisque tu utilises une vieille technique japonaise de ligotage). Enéfé, les cartes ont toutes plus ou moins d'accélérations matérielles pour plus ou moins de couches … Ouhaaaaa c'te découverte :-) Et pour cela on va utiliser l'omniscience de EthTool (noooon pas mii, jete tes cartes!). Dans son vocabulaire à lui de rzoters, il cause avec plein de gros mots :

    • TCP Offload Engine = Moteur de déchargement TCP
    • rx-checksumming /rx (on|off) = Déchargement de la somme de contrôle en réception
    • tx-checksumming / tx (on|off) = Déchargement de la somme de contrôle en émission
    • scatter-gather / sg (on|off) = comme DMA : Entrées et/ou sorties de la carte réseau : transfert direct vers la mémoire principale de la machine, sans intervention du microprocesseur (si ce n'est pour lancer et conclure le transfert).
    • tcp-segmentation-offload / tso (on|off) = Déchargement de la segmentation d'un gros paquet TCP en plusieurs petits (en émission)
    • udp-fragmentation-offload / ufo (on|off) = Déchargement de la fragmentation d'un gros paquet UDP en plusieurs petits (en émission)
    • generic-segmentation-offload / gso on|off) = Déchargement de la segmentation d'un gros paquet TCP en plusieurs petits (en émission)
    • generic-receive-offload / gro (on|off) = Déchargement en fusionnant des petits paquets TCP reçus en un gros paquet pour le système (réception)
    • large-receive-offload / lro (on|off) = Déchargement important à la réception
    • rx-vlan-offload / rxvlan (on|off) = Déchargement de la gestion des Vlan en réception
    • tx-vlan-offload / txvlan (on|off) = Déchargement de la gestion des Vlan en émission
    • ntuple-filters / ntuple (on|off) =
    • receive-hashing / rxhash (on|off) = receive hashing offload

    Ce qui nous intéresse à priori c'est de faire sauter la frag et la seg. Mais allons y gaiement, et ne faisant aucune confiance dans le firmware de la carte réseau, tel de preux chevaliers caquis fasse à l'envahisseur de l'intérieur :

    ethtool -K enp15s0 rx off tx off sg off tso off gso off gro off lro off rxvlan off txvlan off ntuple off rxhash off
    

    Normalement, c'est sans erreur. Plonquée, blam dans la gueule du firmware de la carte rzo, gourdin et massue. Bon maintenant, on va faire chauffer un peu ce processeur… La carte est bien dans cet état :

    [root@transportable log]# ethtool -k
    Features for enp15s0:
    rx-checksumming: off [fixed]
    tx-checksumming: off
            tx-checksum-ipv4: off [fixed]
            tx-checksum-ip-generic: off
            tx-checksum-ipv6: off [fixed]
            tx-checksum-fcoe-crc: off [fixed]
            tx-checksum-sctp: off [fixed]
    scatter-gather: off
            tx-scatter-gather: off
            tx-scatter-gather-fraglist: off [fixed]
    tcp-segmentation-offload: off
            tx-tcp-segmentation: off
            tx-tcp-ecn-segmentation: off [fixed]
            tx-tcp6-segmentation: off
    udp-fragmentation-offload: off [fixed]
    generic-segmentation-offload: off
    generic-receive-offload: off
    large-receive-offload: off [fixed]
    rx-vlan-offload: off
    tx-vlan-offload: off [fixed]
    ntuple-filters: off [fixed]
    receive-hashing: off [fixed]
    highdma: off [fixed]
    rx-vlan-filter: off [fixed]
    vlan-challenged: off [fixed]
    tx-lockless: off [fixed]
    netns-local: off [fixed]
    tx-gso-robust: off [fixed]
    tx-fcoe-segmentation: off [fixed]
    tx-gre-segmentation: off [fixed]
    tx-udp_tnl-segmentation: off [fixed]
    tx-mpls-segmentation: off [fixed]
    fcoe-mtu: off [fixed]
    tx-nocache-copy: on
    loopback: off [fixed]
    rx-fcs: off [fixed]
    rx-all: off [fixed]
    tx-vlan-stag-hw-insert: off [fixed]
    rx-vlan-stag-hw-parse: off [fixed]
    rx-vlan-stag-filter: off [fixed]
    

    débilt-test version commerciale Et aussi :
    debilt-test1 debilt-test2
    42 secondes pour télécharger ~357M & 35 secondes pour envoyer les mêmes données. ça va.

    Concernant la charge CPU pour les opérations réseaux qui étaient auparavant fait par la carte réseau, les experts estiment que 1 Mb/s prend 1 Hz de CPU (donc ~mettons un traffic permanent de ~1Gb/s : il consommera ~1Mhz du premier cpu [sans gso donc]), ça devrait aller tu ne fera pas pédaler dans la semoule ton Quadrandeux cores…

    Bon, zallons y n0w appliquons le paramètre que tu donnes, et seulement lui :

    Dans sysctl.conf (ou systcl.conf.d/myapp.conf)

    net.ipv4.tcp_window_scaling = 0
    net.ipv4.tcp_rmem = 512 1392 4092
    

    Et là, c'est LE DRAME : mon débit plafonne à 50ko/s comme si mon O.N.T. s'était muté en antique modem.

    Alors, quoi ? On continue ? :-)

    • [^] # Re: trucs & machins...

      Posté par  (Mastodon) . Évalué à 3. Dernière modification le 02 novembre 2013 à 02:53.

      Alors j'pense (bien humblement) que tu devrais essayer ceci :

      net.ipv4.tcp_workaround_signed_windows = 1
      net.ipv4.tcp_base_mss = 1056
      net.ipv4.tcp_tso_win_divisor = 1
      net.ipv4.tcp_window_scaling = 1
      

      Pourquoi ? On force la division minimale de paquet à 1 (3 par défaut & gso c'est le mal ). Puis on colle une base pour mss à la valeur voulue (1096 donc, pour toi : Ici, sur la Fibre, j'ai une perte de débit en forçant la mtu et le mss comme cela, forcément : c'est juste pour ton exemple) enfin on s"assure que les wmem & rmem sont correctement positionnées, genre 4096 16384 4194304 (et puis on ajoute le workaround pour considérer que l'autre bout est tout cassé et qu'il traite mal la fenêtre). Je continue d'utiliser CUBIC pour les apps non privilégiées, et la plupart des autres paramètres tcp sont restés par défaut (ce qui ne le sont pas n'entrent pas en ligne de compte ici). Enfin, on fixe la mtu avec ip/ifconfig/sysconfig à 1096. Un tit coup de sysctl -w .net.ipv4.route_flush=1 & de sysctl -f /etc/sysctl.conf. La Nimage :

      Et je vois la vie en 1044
      1044
      1044
      1044
      LOL

      02:01:04.822588 IP 192.168.1.13.59108 > 89.84.127.54.50837: Flags [.], ack 24167556, win 32767, options [nop,nop,TS val 7889144 ecr 656051175], length 0
      02:01:04.822592 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24167556:24168600, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822598 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24168600:24169644, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822603 IP 192.168.1.13.59108 > 89.84.127.54.50837: Flags [.], ack 24169644, win 32767, options [nop,nop,TS val 7889144 ecr 656051175], length 0
      02:01:04.822613 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24169644:24170688, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822617 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24170688:24171732, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822623 IP 192.168.1.13.59108 > 89.84.127.54.50837: Flags [.], ack 24171732, win 32767, options [nop,nop,TS val 7889145 ecr 656051175], length 0
      02:01:04.822624 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24171732:24172776, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822916 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24172776:24173820, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      02:01:04.822952 IP 192.168.1.13.59108 > 89.84.127.54.50837: Flags [.], ack 24173820, win 32767, options [nop,nop,TS val 7889145 ecr 656051175], length 0
      02:01:04.822957 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24173820:24174864, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
      

      Le paramètre essentiel est de commencer avec le retrait des options de la carte réseau. Lorsque cette dernière se charge de la fragmentation alors le noyau va segmenter tes ko en paquets (de 1460 octets par exemple).

      Tandisque si on laisse faire la carte réseau, le noyau va envoyer un gros paquet, et c'est la carte qui va segmenter.

      Donc on ne peux pas faire joujou avec ces valeurs sans au préalable désactiver ces fonctions de la carte : il faut laisser faire le noyau.

      En continuant sur le ton du début de commentaire, c'est un comportement de looser, de windows et autres TOE joyeusetés qui ont essayé de rentrer dans le noyau : retirer cette possibilité au noyau sous le fallacieux prétexte que "ip c'est touchy", alors on colle toute la pile ip dans le firmware de la carte rzo… Non mais ils sont FOUS. Donc, on commence par retirer un maximum de possibilités à la carte réseau, et on va laisser le noyau gérer cela à la place, hein

      Là où ça devient rigolo, c'est qu'avec TCPDUMP tu ne verra pas la même chose, si tu le lances sur le même serveur. Si tu laisses faire la carte, tu verra un gros paquet. Si tu laisses faire le noyau tu verra les paquets fragmentés. Mais pourtant, même en voyant un seul gros paquet, c'est bien des fragments qui seront envoyés sur le réseau.

      Il te faut envoyer tes paquets de tests avec ton application cible. Et aucune autre, au cas les autres feraient bien les choses, c'est souvent le cas sur gnu/linux : tu dois ajuster ces paramètres en fonction de ton application, par touche si besoin. Et sans te fier à un autre type d'envoi pour ces réglages, et en ayant un tcpdump à l'autre bout. Ce sont les deux conditions essentielles. Enfin, il y a perte de débit mais tu t'en fous puisque cette machine ne sert qu'à cette app là.

      En résumé pour ton besoin :

      net.ipv4.tcp_workaround_signed_windows = 1
      net.ipv4.tcp_tso_win_divisor = 1
      net.ipv4.tcp_base_mss = 1056
      
      ifconfig enp15s0 mtu 1096
      

      debilt-test6

      Mes deux cents.
      (j'me coucherai moins con ce soir)

      • [^] # Re: trucs & machins...

        Posté par  . Évalué à 1.

        Dans ton exemple je ne vois nul part que ton client a une taille de fenêtre à 1044 octets:

        02:01:04.822588 IP 192.168.1.13.59108 > 89.84.127.54.50837: Flags [.], ack 24167556, win 32767, options [nop,nop,TS val 7889144 ecr 656051175], length 0
        02:01:04.822592 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24167556:24168600, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044
        02:01:04.822598 IP 89.84.127.54.50837 > 192.168.1.13.59108: Flags [.], seq 24168600:24169644, ack 1, win 14480, options [nop,nop,TS val 656051175 ecr 7889130], length 1044

        C'est bien là mon problème: la taille de fenêtre de mon client est de 1044 octets et mon serveur doit envoyer des segments de 1044 octets.
        Positionne la fenêtre en dessous de la MTU et tu verras ça va fragmenter (je n'ai pas encore découvert à partir de quel seuil ça commence a fragmenter mais il semblerait que positionner une fenêtre plus basse que la MTU soit une mauvaise idée !).

        Ma seule façon d'envoyer du 1044 sur mon serveur c'est de toucher la conf coté client pour lui coller une fenêtre de 2048 octets, chose qui est impossible car je n'ai pas le droit de modifier la configuration du client.

        J'ai essayé tes paramètres mais malheureusement sans succès, mon serveur envoie toujours des paquets de 522 octets (1044/2).
        Nice try! :)

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.