|
@@ -978,6 +978,8 @@ struct netlink_broadcast_data {
|
|
|
int delivered;
|
|
|
gfp_t allocation;
|
|
|
struct sk_buff *skb, *skb2;
|
|
|
+ int (*tx_filter)(struct sock *dsk, struct sk_buff *skb, void *data);
|
|
|
+ void *tx_data;
|
|
|
};
|
|
|
|
|
|
static inline int do_one_broadcast(struct sock *sk,
|
|
@@ -1020,6 +1022,9 @@ static inline int do_one_broadcast(struct sock *sk,
|
|
|
p->failure = 1;
|
|
|
if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
|
|
|
p->delivery_failure = 1;
|
|
|
+ } else if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) {
|
|
|
+ kfree_skb(p->skb2);
|
|
|
+ p->skb2 = NULL;
|
|
|
} else if (sk_filter(sk, p->skb2)) {
|
|
|
kfree_skb(p->skb2);
|
|
|
p->skb2 = NULL;
|
|
@@ -1038,8 +1043,10 @@ out:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|
|
- u32 group, gfp_t allocation)
|
|
|
+int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|
|
+ u32 group, gfp_t allocation,
|
|
|
+ int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
|
|
|
+ void *filter_data)
|
|
|
{
|
|
|
struct net *net = sock_net(ssk);
|
|
|
struct netlink_broadcast_data info;
|
|
@@ -1059,6 +1066,8 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|
|
info.allocation = allocation;
|
|
|
info.skb = skb;
|
|
|
info.skb2 = NULL;
|
|
|
+ info.tx_filter = filter;
|
|
|
+ info.tx_data = filter_data;
|
|
|
|
|
|
/* While we sleep in clone, do not allow to change socket list */
|
|
|
|
|
@@ -1083,6 +1092,14 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|
|
}
|
|
|
return -ESRCH;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(netlink_broadcast_filtered);
|
|
|
+
|
|
|
+int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|
|
+ u32 group, gfp_t allocation)
|
|
|
+{
|
|
|
+ return netlink_broadcast_filtered(ssk, skb, pid, group, allocation,
|
|
|
+ NULL, NULL);
|
|
|
+}
|
|
|
EXPORT_SYMBOL(netlink_broadcast);
|
|
|
|
|
|
struct netlink_set_err_data {
|