diff options
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 55 |
1 files changed, 44 insertions, 11 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 5b33879c6422..b73d4e61c5ac 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -85,6 +85,7 @@ struct netlink_sock { | |||
85 | 85 | ||
86 | #define NETLINK_KERNEL_SOCKET 0x1 | 86 | #define NETLINK_KERNEL_SOCKET 0x1 |
87 | #define NETLINK_RECV_PKTINFO 0x2 | 87 | #define NETLINK_RECV_PKTINFO 0x2 |
88 | #define NETLINK_BROADCAST_SEND_ERROR 0x4 | ||
88 | 89 | ||
89 | static inline struct netlink_sock *nlk_sk(struct sock *sk) | 90 | static inline struct netlink_sock *nlk_sk(struct sock *sk) |
90 | { | 91 | { |
@@ -995,12 +996,15 @@ static inline int do_one_broadcast(struct sock *sk, | |||
995 | netlink_overrun(sk); | 996 | netlink_overrun(sk); |
996 | /* Clone failed. Notify ALL listeners. */ | 997 | /* Clone failed. Notify ALL listeners. */ |
997 | p->failure = 1; | 998 | p->failure = 1; |
999 | if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) | ||
1000 | p->delivery_failure = 1; | ||
998 | } else if (sk_filter(sk, p->skb2)) { | 1001 | } else if (sk_filter(sk, p->skb2)) { |
999 | kfree_skb(p->skb2); | 1002 | kfree_skb(p->skb2); |
1000 | p->skb2 = NULL; | 1003 | p->skb2 = NULL; |
1001 | } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { | 1004 | } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { |
1002 | netlink_overrun(sk); | 1005 | netlink_overrun(sk); |
1003 | p->delivery_failure = 1; | 1006 | if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) |
1007 | p->delivery_failure = 1; | ||
1004 | } else { | 1008 | } else { |
1005 | p->congested |= val; | 1009 | p->congested |= val; |
1006 | p->delivered = 1; | 1010 | p->delivered = 1; |
@@ -1045,10 +1049,9 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, | |||
1045 | 1049 | ||
1046 | netlink_unlock_table(); | 1050 | netlink_unlock_table(); |
1047 | 1051 | ||
1048 | if (info.skb2) | 1052 | kfree_skb(info.skb2); |
1049 | kfree_skb(info.skb2); | ||
1050 | 1053 | ||
1051 | if (info.delivery_failure || info.failure) | 1054 | if (info.delivery_failure) |
1052 | return -ENOBUFS; | 1055 | return -ENOBUFS; |
1053 | 1056 | ||
1054 | if (info.delivered) { | 1057 | if (info.delivered) { |
@@ -1088,6 +1091,13 @@ out: | |||
1088 | return 0; | 1091 | return 0; |
1089 | } | 1092 | } |
1090 | 1093 | ||
1094 | /** | ||
1095 | * netlink_set_err - report error to broadcast listeners | ||
1096 | * @ssk: the kernel netlink socket, as returned by netlink_kernel_create() | ||
1097 | * @pid: the PID of a process that we want to skip (if any) | ||
1098 | * @groups: the broadcast group that will notice the error | ||
1099 | * @code: error code, must be negative (as usual in kernelspace) | ||
1100 | */ | ||
1091 | void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) | 1101 | void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) |
1092 | { | 1102 | { |
1093 | struct netlink_set_err_data info; | 1103 | struct netlink_set_err_data info; |
@@ -1097,7 +1107,8 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) | |||
1097 | info.exclude_sk = ssk; | 1107 | info.exclude_sk = ssk; |
1098 | info.pid = pid; | 1108 | info.pid = pid; |
1099 | info.group = group; | 1109 | info.group = group; |
1100 | info.code = code; | 1110 | /* sk->sk_err wants a positive error value */ |
1111 | info.code = -code; | ||
1101 | 1112 | ||
1102 | read_lock(&nl_table_lock); | 1113 | read_lock(&nl_table_lock); |
1103 | 1114 | ||
@@ -1164,6 +1175,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
1164 | err = 0; | 1175 | err = 0; |
1165 | break; | 1176 | break; |
1166 | } | 1177 | } |
1178 | case NETLINK_BROADCAST_ERROR: | ||
1179 | if (val) | ||
1180 | nlk->flags |= NETLINK_BROADCAST_SEND_ERROR; | ||
1181 | else | ||
1182 | nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR; | ||
1183 | err = 0; | ||
1184 | break; | ||
1167 | default: | 1185 | default: |
1168 | err = -ENOPROTOOPT; | 1186 | err = -ENOPROTOOPT; |
1169 | } | 1187 | } |
@@ -1196,6 +1214,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, | |||
1196 | return -EFAULT; | 1214 | return -EFAULT; |
1197 | err = 0; | 1215 | err = 0; |
1198 | break; | 1216 | break; |
1217 | case NETLINK_BROADCAST_ERROR: | ||
1218 | if (len < sizeof(int)) | ||
1219 | return -EINVAL; | ||
1220 | len = sizeof(int); | ||
1221 | val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0; | ||
1222 | if (put_user(len, optlen) || | ||
1223 | put_user(val, optval)) | ||
1224 | return -EFAULT; | ||
1225 | err = 0; | ||
1226 | break; | ||
1199 | default: | 1227 | default: |
1200 | err = -ENOPROTOOPT; | 1228 | err = -ENOPROTOOPT; |
1201 | } | 1229 | } |
@@ -1522,8 +1550,7 @@ EXPORT_SYMBOL(netlink_set_nonroot); | |||
1522 | 1550 | ||
1523 | static void netlink_destroy_callback(struct netlink_callback *cb) | 1551 | static void netlink_destroy_callback(struct netlink_callback *cb) |
1524 | { | 1552 | { |
1525 | if (cb->skb) | 1553 | kfree_skb(cb->skb); |
1526 | kfree_skb(cb->skb); | ||
1527 | kfree(cb); | 1554 | kfree(cb); |
1528 | } | 1555 | } |
1529 | 1556 | ||
@@ -1740,12 +1767,18 @@ int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 pid, | |||
1740 | exclude_pid = pid; | 1767 | exclude_pid = pid; |
1741 | } | 1768 | } |
1742 | 1769 | ||
1743 | /* errors reported via destination sk->sk_err */ | 1770 | /* errors reported via destination sk->sk_err, but propagate |
1744 | nlmsg_multicast(sk, skb, exclude_pid, group, flags); | 1771 | * delivery errors if NETLINK_BROADCAST_ERROR flag is set */ |
1772 | err = nlmsg_multicast(sk, skb, exclude_pid, group, flags); | ||
1745 | } | 1773 | } |
1746 | 1774 | ||
1747 | if (report) | 1775 | if (report) { |
1748 | err = nlmsg_unicast(sk, skb, pid); | 1776 | int err2; |
1777 | |||
1778 | err2 = nlmsg_unicast(sk, skb, pid); | ||
1779 | if (!err || err == -ESRCH) | ||
1780 | err = err2; | ||
1781 | } | ||
1749 | 1782 | ||
1750 | return err; | 1783 | return err; |
1751 | } | 1784 | } |