backports: fix the extack backport for dumps
[openwrt/staging/blogic.git] / backport / compat / backport-4.12.c
1 /*
2 * Copyright 2017 Intel Deutschland GmbH
3 */
4 #include <net/genetlink.h>
5 #include <net/sock.h>
6
7 enum nlmsgerr_attrs {
8 NLMSGERR_ATTR_UNUSED,
9 NLMSGERR_ATTR_MSG,
10 NLMSGERR_ATTR_OFFS,
11 NLMSGERR_ATTR_COOKIE,
12 __NLMSGERR_ATTR_MAX,
13 NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
14 };
15
16 #define NLM_F_CAPPED 0x100 /* request was capped */
17 #define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
18
19 struct bp_extack_genl_family {
20 struct genl_family family;
21 struct genl_family *real_family;
22 struct list_head list;
23
24 struct genl_ops ops[];
25 };
26
27 static LIST_HEAD(copies_list);
28 static DEFINE_MUTEX(copies_mutex);
29
30 static const struct nla_policy extack_dummy_policy[1] = {};
31
32 static struct bp_extack_genl_family *get_copy(__genl_const struct genl_ops *op)
33 {
34 do {
35 op--;
36 } while (op->policy != extack_dummy_policy);
37
38 return container_of(op, struct bp_extack_genl_family, ops[0]);
39 }
40
41 static int extack_pre_doit(__genl_const struct genl_ops *ops,
42 struct sk_buff *skb,
43 struct genl_info *info)
44 {
45 struct netlink_ext_ack *extack = kzalloc(sizeof(*extack), GFP_KERNEL);
46 struct bp_extack_genl_family *copy = get_copy(ops);
47 struct genl_ops *real_ops;
48 int err;
49
50 __bp_genl_info_userhdr_set(info, extack);
51
52 if (!extack) {
53 __bp_genl_info_userhdr_set(info, ERR_PTR(-ENOMEM));
54 return -ENOMEM;
55 }
56
57 real_ops = (void *)&copy->real_family->ops[ops - &copy->ops[1]];
58 extack->__bp_genl_real_ops = real_ops;
59
60 if (copy->real_family->pre_doit)
61 err = copy->real_family->pre_doit(real_ops, skb, info);
62 else
63 err = 0;
64
65 if (err) {
66 __bp_genl_info_userhdr_set(info, ERR_PTR(err));
67 kfree(extack);
68 }
69
70 return err;
71 }
72
73 static void extack_netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh,
74 int err, const struct netlink_ext_ack *extack)
75 {
76 struct sk_buff *skb;
77 struct nlmsghdr *rep;
78 struct nlmsgerr *errmsg;
79 size_t payload = sizeof(*errmsg);
80 size_t tlvlen = 0;
81 unsigned int flags = 0;
82 /* backports assumes everyone supports this - libnl does so it's true */
83 bool nlk_has_extack = true;
84
85 /* Error messages get the original request appened, unless the user
86 * requests to cap the error message, and get extra error data if
87 * requested.
88 * (ignored in backports)
89 */
90 if (err) {
91 if (1)
92 payload += nlmsg_len(nlh);
93 else
94 flags |= NLM_F_CAPPED;
95 if (nlk_has_extack && extack) {
96 if (extack->_msg)
97 tlvlen += nla_total_size(strlen(extack->_msg) + 1);
98 if (extack->bad_attr)
99 tlvlen += nla_total_size(sizeof(u32));
100 }
101 } else {
102 flags |= NLM_F_CAPPED;
103
104 if (nlk_has_extack && extack && extack->cookie_len)
105 tlvlen += nla_total_size(extack->cookie_len);
106 }
107
108 if (tlvlen)
109 flags |= NLM_F_ACK_TLVS;
110
111 skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
112 if (!skb) {
113 NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
114 NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);
115 return;
116 }
117
118 rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
119 NLMSG_ERROR, payload, flags);
120 errmsg = nlmsg_data(rep);
121 errmsg->error = err;
122 memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
123
124 if (nlk_has_extack && extack) {
125 if (err) {
126 if (extack->_msg)
127 WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
128 extack->_msg));
129 if (extack->bad_attr &&
130 !WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
131 (u8 *)extack->bad_attr >= in_skb->data +
132 in_skb->len))
133 WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
134 (u8 *)extack->bad_attr -
135 in_skb->data));
136 } else {
137 if (extack->cookie_len)
138 WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE,
139 extack->cookie_len,
140 extack->cookie));
141 }
142 }
143
144 nlmsg_end(skb, rep);
145
146 netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
147 }
148
149 static int extack_doit(struct sk_buff *skb, struct genl_info *info)
150 {
151 struct genl_ops *real_ops;
152 int err;
153
154 /* older kernels have a bug here */
155 if (IS_ERR(__bp_genl_info_userhdr(info))) {
156 extack_netlink_ack(skb, info->nlhdr,
157 PTR_ERR(__bp_genl_info_userhdr(info)),
158 genl_info_extack(info));
159 goto out;
160 }
161
162 real_ops = genl_info_extack(info)->__bp_genl_real_ops;
163 err = real_ops->doit(skb, info);
164
165 if (err == -EINTR)
166 return err;
167
168 if (info->nlhdr->nlmsg_flags & NLM_F_ACK || err)
169 extack_netlink_ack(skb, info->nlhdr, err,
170 genl_info_extack(info));
171
172 out:
173 /* suppress sending ACK from normal netlink code */
174 info->nlhdr->nlmsg_flags &= ~NLM_F_ACK;
175 return 0;
176 }
177
178 static void extack_post_doit(__genl_const struct genl_ops *ops,
179 struct sk_buff *skb,
180 struct genl_info *info)
181 {
182 void (*post_doit)(__genl_const struct genl_ops *ops,
183 struct sk_buff *skb,
184 struct genl_info *info);
185
186 post_doit = get_copy(ops)->real_family->post_doit;
187
188 if (post_doit)
189 post_doit(ops, skb, info);
190 kfree(__bp_genl_info_userhdr(info));
191 }
192
193 int bp_extack_genl_register_family(struct genl_family *family)
194 {
195 unsigned int size = sizeof(struct bp_extack_genl_family) +
196 sizeof(family->ops[0]) * (family->n_ops + 1);
197 struct bp_extack_genl_family *copy;
198 int i, err;
199
200 copy = kzalloc(size, GFP_KERNEL);
201 if (!copy)
202 return -ENOMEM;
203
204 copy->family = *family;
205 copy->real_family = family;
206 copy->family.ops = &copy->ops[1];
207
208 for (i = 0; i < family->n_ops; i++) {
209 copy->ops[i + 1] = family->ops[i];
210 if (family->ops[i].doit)
211 copy->ops[i + 1].doit = extack_doit;
212 }
213
214 copy->ops[0].policy = extack_dummy_policy;
215
216 copy->family.pre_doit = extack_pre_doit;
217 copy->family.post_doit = extack_post_doit;
218
219 err = __real_bp_extack_genl_register_family(&copy->family);
220 if (err) {
221 kfree(copy);
222 return err;
223 }
224
225 /* copy this since the family might access it directly */
226 family->attrbuf = copy->family.attrbuf;
227
228 mutex_lock(&copies_mutex);
229 list_add_tail(&copy->list, &copies_list);
230 mutex_unlock(&copies_mutex);
231
232 return 0;
233 }
234 EXPORT_SYMBOL_GPL(bp_extack_genl_register_family);
235
236 int bp_extack_genl_unregister_family(struct genl_family *family)
237 {
238 struct bp_extack_genl_family *tmp, *copy = NULL;
239 int err;
240
241 mutex_lock(&copies_mutex);
242 list_for_each_entry(tmp, &copies_list, list) {
243 if (tmp->real_family == family) {
244 copy = tmp;
245 break;
246 }
247 }
248 if (copy)
249 list_del(&copy->list);
250 mutex_unlock(&copies_mutex);
251
252 if (!copy)
253 return -ENOENT;
254
255 err = __real_bp_extack_genl_unregister_family(&copy->family);
256 WARN_ON(err);
257 kfree(copy);
258
259 return 0;
260 }
261 EXPORT_SYMBOL_GPL(bp_extack_genl_unregister_family);