forked from mirrors/linux
		
	netlink: add NETLINK_BROADCAST_ERROR socket option
This patch adds NETLINK_BROADCAST_ERROR which is a netlink socket option that the listener can set to make netlink_broadcast() return errors in the delivery to the caller. This option is useful if the caller of netlink_broadcast() do something with the result of the message delivery, like in ctnetlink where it drops a network packet if the event delivery failed, this is used to enable reliable logging and state-synchronization. If this socket option is not set, netlink_broadcast() only reports ESRCH errors and silently ignore ENOBUFS errors, which is what most netlink_broadcast() callers should do. This socket option is based on a suggestion from Patrick McHardy. Patrick McHardy can exchange this patch for a beer from me ;). Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									2d96cf8cdf
								
							
						
					
					
						commit
						be0c22a46c
					
				
					 2 changed files with 24 additions and 2 deletions
				
			
		| 
						 | 
					@ -103,6 +103,7 @@ struct nlmsgerr
 | 
				
			||||||
#define NETLINK_ADD_MEMBERSHIP	1
 | 
					#define NETLINK_ADD_MEMBERSHIP	1
 | 
				
			||||||
#define NETLINK_DROP_MEMBERSHIP	2
 | 
					#define NETLINK_DROP_MEMBERSHIP	2
 | 
				
			||||||
#define NETLINK_PKTINFO		3
 | 
					#define NETLINK_PKTINFO		3
 | 
				
			||||||
 | 
					#define NETLINK_BROADCAST_ERROR	4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct nl_pktinfo
 | 
					struct nl_pktinfo
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,7 @@ struct netlink_sock {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NETLINK_KERNEL_SOCKET	0x1
 | 
					#define NETLINK_KERNEL_SOCKET	0x1
 | 
				
			||||||
#define NETLINK_RECV_PKTINFO	0x2
 | 
					#define NETLINK_RECV_PKTINFO	0x2
 | 
				
			||||||
 | 
					#define NETLINK_BROADCAST_SEND_ERROR	0x4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct netlink_sock *nlk_sk(struct sock *sk)
 | 
					static inline struct netlink_sock *nlk_sk(struct sock *sk)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -995,11 +996,14 @@ static inline int do_one_broadcast(struct sock *sk,
 | 
				
			||||||
		netlink_overrun(sk);
 | 
							netlink_overrun(sk);
 | 
				
			||||||
		/* Clone failed. Notify ALL listeners. */
 | 
							/* Clone failed. Notify ALL listeners. */
 | 
				
			||||||
		p->failure = 1;
 | 
							p->failure = 1;
 | 
				
			||||||
 | 
							if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
 | 
				
			||||||
 | 
								p->delivery_failure = 1;
 | 
				
			||||||
	} else if (sk_filter(sk, p->skb2)) {
 | 
						} else if (sk_filter(sk, p->skb2)) {
 | 
				
			||||||
		kfree_skb(p->skb2);
 | 
							kfree_skb(p->skb2);
 | 
				
			||||||
		p->skb2 = NULL;
 | 
							p->skb2 = NULL;
 | 
				
			||||||
	} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
 | 
						} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
 | 
				
			||||||
		netlink_overrun(sk);
 | 
							netlink_overrun(sk);
 | 
				
			||||||
 | 
							if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
 | 
				
			||||||
			p->delivery_failure = 1;
 | 
								p->delivery_failure = 1;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		p->congested |= val;
 | 
							p->congested |= val;
 | 
				
			||||||
| 
						 | 
					@ -1048,7 +1052,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
 | 
				
			||||||
	if (info.skb2)
 | 
						if (info.skb2)
 | 
				
			||||||
		kfree_skb(info.skb2);
 | 
							kfree_skb(info.skb2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (info.delivery_failure || info.failure)
 | 
						if (info.delivery_failure)
 | 
				
			||||||
		return -ENOBUFS;
 | 
							return -ENOBUFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (info.delivered) {
 | 
						if (info.delivered) {
 | 
				
			||||||
| 
						 | 
					@ -1163,6 +1167,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
		err = 0;
 | 
							err = 0;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						case NETLINK_BROADCAST_ERROR:
 | 
				
			||||||
 | 
							if (val)
 | 
				
			||||||
 | 
								nlk->flags |= NETLINK_BROADCAST_SEND_ERROR;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR;
 | 
				
			||||||
 | 
							err = 0;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1195,6 +1206,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
 | 
				
			||||||
			return -EFAULT;
 | 
								return -EFAULT;
 | 
				
			||||||
		err = 0;
 | 
							err = 0;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case NETLINK_BROADCAST_ERROR:
 | 
				
			||||||
 | 
							if (len < sizeof(int))
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							len = sizeof(int);
 | 
				
			||||||
 | 
							val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0;
 | 
				
			||||||
 | 
							if (put_user(len, optlen) ||
 | 
				
			||||||
 | 
							    put_user(val, optval))
 | 
				
			||||||
 | 
								return -EFAULT;
 | 
				
			||||||
 | 
							err = 0;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue