Eh bien, je ne sais pas exactement quel est le problème, mais je vais essayer de trouver la bonne direction pour le résoudre.
Le ENOBUFS
code est renvoyé lorsque sk_alloc()
ou dst_alloc()
est raté. Je ne trouve aucune autre occurrence de ENOBUFS
dans le code source lié aux sockets.
De plus, je ne trouve aucun chemin à partir du SYSCALL_DEFINE3(connect)
à sk_alloc()
, et je pense que le socket ne devrait pas être alloué pendant connect()
appel où vous obtenez l'erreur, donc je pense qu'il est peu probable que le sk_alloc()
causé le problème.
Le dst_alloc()
est probablement utilisé pour vérifier les itinéraires pendant le connect()
, je ne trouve pas le chemin exact, il doit être quelque part à l'intérieur :SYSCALL_DEFINE3(connect)
-> .connect()
-> ip4_datagram_connect()
-> ip_route_connect()
Le dst_alloc()
alloue une entrée dans un cache SLAB correspondant, et il peut en fait échouer si le cache est plein. En fait, les anciennes entrées doivent être purgées si cela se produit, mais il y a peut-être des cas où cela renvoie toujours une erreur.
Je pense donc que vous pouvez vous déplacer dans cette direction. La taille du cache dst peut changer jusqu'à /proc/sys/net/ipv4/route/max_size
. Tout d'abord, vérifiez si le paramètre (ou tout autre paramètre dans sys.net.ipv4.route
) est modifié par "des ajustements TCP aléatoires affichés sur Google".
Dans les noyaux antérieurs à 3.6, vous auriez pu être touché par ENOBUFS pour le trafic IPv4/v6 normal, lorsque la limite net.ipv4.route.max_size ou net.ipv6.route.max_size était épuisée, en conséquence.
À partir du noyau 3.6, le cache de routage a été supprimé et net.ipv4.route.max_size a perdu son influence sur la quantité d'entrées dst. Donc, généralement, ce ne serait plus possible.
Cependant, vous pouvez toujours rencontrer cette erreur comme moi, lors de l'utilisation d'IPSec. Après un certain nombre de tunnels IPSec créés, je n'ai pas pu envoyer de ping à l'hôte distant :
# ping 10.100.0.1
connect: No buffer space available
ping à partir d'iputils crée un descripteur de fichier de test et utilise connect() dessus pour lier l'adresse IP dst. Lorsque cela se produit, une entrée de cache dst pour l'AF donné est créée par le noyau, et la limite d'entrées de cache xfrm4 dst était déjà épuisée dans mon cas. Cette limite est contrôlée par le paramètre sysctl :
xfrm4_gc_thresh - INTEGER
The threshold at which we will start garbage collecting for IPv4
destination cache entries. At twice this value the system will
refuse new allocations.
J'avais rencontré cela en utilisant le noyau 3.10.59, où la limite par défaut est très basse - 1024. À partir du noyau 3.10.83, cette limite a été augmentée à 32768 et serait beaucoup plus difficile à atteindre.
Donc, j'ai émis :
# sysctl net.ipv4.xfrm4_gc_thresh=32768
et ça a fait l'affaire pour moi.
Chemin approximatif dans le noyau pour mon cas avec IPSec :
ip4_datagram_connect() -> ip_route_connect() -> ip_route_output_flow() ->
xfrm_lookup() -> xfrm_resolve_and_create_bundle() ->
... -> xfrm_alloc_dst() -> dst_alloc() with xfrm4_dst_ops, where gc is set.