Bug 216986

Summary: sendmsg returns EINVAL when neighbor table is full
Product: Networking Reporter: prashant (singhpra)
Component: IPV4Assignee: Stephen Hemminger (stephen)
Status: NEW ---    
Severity: normal    
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 5.2.60 Subsystem:
Regression: No Bisected commit-id:

Description prashant 2023-02-01 02:21:14 UTC
On one of our linux boxes, we happen to hit the net.ipv4.neigh.default.gc_thresh3
limit and saw "neighbour: arp_cache: neighbor table overflow! ".

Subsequent to this, we saw EINVAL error for some of the applications that were trying to do a sendmsg. The applications treats the EINVAL error as fatal (especially when sendmsg was working with the same set of ARGS earlier on the system).

The question here is should the kernel neighbor table state result in an error like EINVAL for the application where the arguments provided by the application looks valid and have been working so far. Is there any other reason that justifies the EINVAL returned for this case by kernel?

Looking further into the code it seems like in the function ip_finish_output2, even on the latest kernel, post the checks for neighbor entry, the status of ip_neigh_for_gw for error is not used, which in this case would be ENOBUFS and I would think could also be a possible alternative to return here instead of EINVAL at the end of the function. 

ip_finish_output2:
        neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);  
                    if (!IS_ERR(neigh)) {
                   …
               }                                                                             
        ==>perhaps can return this neigh if IS_ERR is true
        net_dbg_ratelimited("%s: No header cache and no neighbour!\n",
                                                                 __func__);
        kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
        return -EINVAL;==> here
 

___neigh_create:
	n = neigh_alloc(tbl, dev, flags, exempt_from_gc);
	trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc);
	if (!n) {
		rc = ERR_PTR(-ENOBUFS); ==> returns ENOBUFS
		goto out;
	}