2372a118c82dff27e28812364d55b9276e526caf
[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 (nlk_has_extack && extack && extack->_msg)
91 tlvlen += nla_total_size(strlen(extack->_msg) + 1);
92
93 if (err) {
94 if (1)
95 payload += nlmsg_len(nlh);
96 else
97 flags |= NLM_F_CAPPED;
98 if (nlk_has_extack && extack && extack->bad_attr)
99 tlvlen += nla_total_size(sizeof(u32));
100 } else {
101 flags |= NLM_F_CAPPED;
102
103 if (nlk_has_extack && extack && extack->cookie_len)
104 tlvlen += nla_total_size(extack->cookie_len);
105 }
106
107 if (tlvlen)
108 flags |= NLM_F_ACK_TLVS;
109
110 skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
111 if (!skb) {
112 NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
113 NETLINK_CB(in_skb).sk->sk_error_report(NETLINK_CB(in_skb).sk);
114 return;
115 }
116
117 rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
118 NLMSG_ERROR, payload, flags);
119 errmsg = nlmsg_data(rep);
120 errmsg->error = err;
121 memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
122
123 if (nlk_has_extack && extack) {
124 if (extack->_msg) {
125 WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
126 extack->_msg));
127 }
128 if (err) {
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->id = copy->family.id;
227 family->attrbuf = copy->family.attrbuf;
228 #if LINUX_VERSION_IS_GEQ(3,13,0)
229 family->mcgrp_offset = copy->family.mcgrp_offset;
230 #endif
231
232 mutex_lock(&copies_mutex);
233 list_add_tail(&copy->list, &copies_list);
234 mutex_unlock(&copies_mutex);
235
236 return 0;
237 }
238 EXPORT_SYMBOL_GPL(bp_extack_genl_register_family);
239
240 int bp_extack_genl_unregister_family(struct genl_family *family)
241 {
242 struct bp_extack_genl_family *tmp, *copy = NULL;
243 int err;
244
245 mutex_lock(&copies_mutex);
246 list_for_each_entry(tmp, &copies_list, list) {
247 if (tmp->real_family == family) {
248 copy = tmp;
249 break;
250 }
251 }
252 if (copy)
253 list_del(&copy->list);
254 mutex_unlock(&copies_mutex);
255
256 if (!copy)
257 return -ENOENT;
258
259 err = __real_bp_extack_genl_unregister_family(&copy->family);
260 WARN_ON(err);
261 kfree(copy);
262
263 return 0;
264 }
265 EXPORT_SYMBOL_GPL(bp_extack_genl_unregister_family);