Beh, non so quale sia esattamente il problema, ma cercherò di trovare la direzione giusta per risolverlo.
Lo ENOBUFS
code viene restituito quando sk_alloc()
o dst_alloc()
è fallito. Non riesco a trovare altre occorrenze di ENOBUFS
nel codice sorgente relativo ai socket.
Inoltre non riesco a trovare alcun percorso dal SYSCALL_DEFINE3(connect)
a sk_alloc()
, e penso che il socket non dovrebbe essere allocato durante connect()
call dove ricevi l'errore, quindi penso che non sia probabile che il sk_alloc()
ha causato il problema.
L'dst_alloc()
è probabilmente usato per controllare i percorsi durante il connect()
, non riesco a trovare il percorso esatto, deve essere da qualche parte all'interno:SYSCALL_DEFINE3(connect)
-> .connect()
-> ip4_datagram_connect()
-> ip_route_connect()
Il dst_alloc()
alloca una voce in una cache SLAB corrispondente e potrebbe effettivamente fallire se la cache è piena. In realtà le vecchie voci dovrebbero essere eliminate se ciò accade, ma forse ci sono casi in cui restituisce ancora un errore.
Quindi penso che tu possa muoverti in questa direzione. La dimensione della cache dst potrebbe essere cambiata attraverso /proc/sys/net/ipv4/route/max_size
. Innanzitutto, controlla se l'impostazione (o qualsiasi altra impostazione in sys.net.ipv4.route
) viene modificato da "modifiche TCP casuali mostrate su Google".
Nei kernel precedenti alla 3.6, potevi essere colpito da ENOBUFS per il normale traffico IPv4/v6, quando il limite net.ipv4.route.max_size o net.ipv6.route.max_size veniva esaurito, di conseguenza.
A partire dal kernel 3.6, la cache di routing è stata rimossa e net.ipv4.route.max_size ha perso la sua influenza sulla quantità di voci dst. Quindi, generalmente, ciò non sarebbe più possibile.
Tuttavia, puoi ancora incorrere in questo errore come me, quando usi IPSec. Dopo un certo numero di tunnel IPSec creati, non sono riuscito a eseguire il ping dell'host remoto:
# ping 10.100.0.1
connect: No buffer space available
ping da iputils crea un descrittore di file di test e utilizza connect() su di esso per associare l'ip dst. Quando accade, la voce della cache dst per il dato AF viene creata dal kernel e il limite delle voci della cache xfrm4 dst era già esaurito nel mio caso. Questo limite è controllato dall'impostazione 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.
Mi sono imbattuto in questo usando il kernel 3.10.59, dove il limite predefinito è molto basso - 1024. A partire dal kernel 3.10.83, questo limite è stato aumentato a 32768, e sarebbe molto più difficile da raggiungere.
Quindi, ho emesso:
# sysctl net.ipv4.xfrm4_gc_thresh=32768
e ha fatto la cosa per me.
Percorso approssimativo nel kernel per il mio caso con 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.