diff options
author | Richard Alpe <richard.alpe@ericsson.com> | 2015-02-09 03:50:04 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-09 16:20:47 -0500 |
commit | d0796d1ef63deb38147729664691ba3090930b26 (patch) | |
tree | b227569cf4d6cd2250000095f1a1867f86d08854 /net/tipc | |
parent | bfb3e5dd8dfd84dfd13649393abab63e43267b00 (diff) |
tipc: convert legacy nl bearer dump to nl compat
Introduce a framework for dumping netlink data from the new netlink
API and formatting it to the old legacy API format. This is done by
looping the dump data and calling a format handler for each entity, in
this case a bearer.
We dump until either all data is dumped or we reach the limited buffer
size of the legacy API. Remember, the legacy API doesn't scale.
In this commit we convert TIPC_CMD_GET_BEARER_NAMES to use the compat
layer.
Signed-off-by: Richard Alpe <richard.alpe@ericsson.com>
Reviewed-by: Erik Hugne <erik.hugne@ericsson.com>
Reviewed-by: Ying Xue <ying.xue@windriver.com>
Reviewed-by: Jon Maloy <jon.maloy@ericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r-- | net/tipc/bearer.c | 29 | ||||
-rw-r--r-- | net/tipc/bearer.h | 1 | ||||
-rw-r--r-- | net/tipc/config.c | 3 | ||||
-rw-r--r-- | net/tipc/netlink_compat.c | 274 |
4 files changed, 273 insertions, 34 deletions
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 35d400e8c2e5..7a9e29641e61 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c | |||
@@ -205,35 +205,6 @@ struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name) | |||
205 | return NULL; | 205 | return NULL; |
206 | } | 206 | } |
207 | 207 | ||
208 | /** | ||
209 | * tipc_bearer_get_names - record names of bearers in buffer | ||
210 | */ | ||
211 | struct sk_buff *tipc_bearer_get_names(struct net *net) | ||
212 | { | ||
213 | struct tipc_net *tn = net_generic(net, tipc_net_id); | ||
214 | struct sk_buff *buf; | ||
215 | struct tipc_bearer *b; | ||
216 | int i, j; | ||
217 | |||
218 | buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME)); | ||
219 | if (!buf) | ||
220 | return NULL; | ||
221 | |||
222 | for (i = 0; media_info_array[i] != NULL; i++) { | ||
223 | for (j = 0; j < MAX_BEARERS; j++) { | ||
224 | b = rtnl_dereference(tn->bearer_list[j]); | ||
225 | if (!b) | ||
226 | continue; | ||
227 | if (b->media == media_info_array[i]) { | ||
228 | tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME, | ||
229 | b->name, | ||
230 | strlen(b->name) + 1); | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | return buf; | ||
235 | } | ||
236 | |||
237 | void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest) | 208 | void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest) |
238 | { | 209 | { |
239 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 210 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index c035e3e24764..956858276d93 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h | |||
@@ -205,7 +205,6 @@ void tipc_disable_l2_media(struct tipc_bearer *b); | |||
205 | int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, | 205 | int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, |
206 | struct tipc_bearer *b, struct tipc_media_addr *dest); | 206 | struct tipc_bearer *b, struct tipc_media_addr *dest); |
207 | 207 | ||
208 | struct sk_buff *tipc_bearer_get_names(struct net *net); | ||
209 | void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); | 208 | void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest); |
210 | void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); | 209 | void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); |
211 | struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name); | 210 | struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name); |
diff --git a/net/tipc/config.c b/net/tipc/config.c index 6873360cda53..52e84b0ac48a 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c | |||
@@ -252,9 +252,6 @@ struct sk_buff *tipc_cfg_do_cmd(struct net *net, u32 orig_node, u16 cmd, | |||
252 | rep_tlv_buf = tipc_nametbl_get(net, req_tlv_area, | 252 | rep_tlv_buf = tipc_nametbl_get(net, req_tlv_area, |
253 | req_tlv_space); | 253 | req_tlv_space); |
254 | break; | 254 | break; |
255 | case TIPC_CMD_GET_BEARER_NAMES: | ||
256 | rep_tlv_buf = tipc_bearer_get_names(net); | ||
257 | break; | ||
258 | case TIPC_CMD_GET_MEDIA_NAMES: | 255 | case TIPC_CMD_GET_MEDIA_NAMES: |
259 | rep_tlv_buf = tipc_media_get_names(); | 256 | rep_tlv_buf = tipc_media_get_names(); |
260 | break; | 257 | break; |
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index f752854c8b10..bd75ea290e3a 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c | |||
@@ -33,9 +33,265 @@ | |||
33 | 33 | ||
34 | #include "core.h" | 34 | #include "core.h" |
35 | #include "config.h" | 35 | #include "config.h" |
36 | #include "bearer.h" | ||
36 | #include <net/genetlink.h> | 37 | #include <net/genetlink.h> |
37 | #include <linux/tipc_config.h> | 38 | #include <linux/tipc_config.h> |
38 | 39 | ||
40 | /* The legacy API had an artificial message length limit called | ||
41 | * ULTRA_STRING_MAX_LEN. | ||
42 | */ | ||
43 | #define ULTRA_STRING_MAX_LEN 32768 | ||
44 | |||
45 | #define TIPC_SKB_MAX TLV_SPACE(ULTRA_STRING_MAX_LEN) | ||
46 | |||
47 | #define REPLY_TRUNCATED "<truncated>\n" | ||
48 | |||
49 | struct tipc_nl_compat_msg { | ||
50 | u16 cmd; | ||
51 | int rep_size; | ||
52 | struct sk_buff *rep; | ||
53 | struct tlv_desc *req; | ||
54 | struct sock *dst_sk; | ||
55 | }; | ||
56 | |||
57 | struct tipc_nl_compat_cmd_dump { | ||
58 | int (*dumpit)(struct sk_buff *, struct netlink_callback *); | ||
59 | int (*format)(struct tipc_nl_compat_msg *msg, struct nlattr **attrs); | ||
60 | }; | ||
61 | |||
62 | static int tipc_skb_tailroom(struct sk_buff *skb) | ||
63 | { | ||
64 | int tailroom; | ||
65 | int limit; | ||
66 | |||
67 | tailroom = skb_tailroom(skb); | ||
68 | limit = TIPC_SKB_MAX - skb->len; | ||
69 | |||
70 | if (tailroom < limit) | ||
71 | return tailroom; | ||
72 | |||
73 | return limit; | ||
74 | } | ||
75 | |||
76 | static int tipc_add_tlv(struct sk_buff *skb, u16 type, void *data, u16 len) | ||
77 | { | ||
78 | struct tlv_desc *tlv = (struct tlv_desc *)skb_tail_pointer(skb); | ||
79 | |||
80 | if (tipc_skb_tailroom(skb) < TLV_SPACE(len)) | ||
81 | return -EMSGSIZE; | ||
82 | |||
83 | skb_put(skb, TLV_SPACE(len)); | ||
84 | tlv->tlv_type = htons(type); | ||
85 | tlv->tlv_len = htons(TLV_LENGTH(len)); | ||
86 | if (len && data) | ||
87 | memcpy(TLV_DATA(tlv), data, len); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static struct sk_buff *tipc_tlv_alloc(int size) | ||
93 | { | ||
94 | int hdr_len; | ||
95 | struct sk_buff *buf; | ||
96 | |||
97 | size = TLV_SPACE(size); | ||
98 | hdr_len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); | ||
99 | |||
100 | buf = alloc_skb(hdr_len + size, GFP_KERNEL); | ||
101 | if (!buf) | ||
102 | return NULL; | ||
103 | |||
104 | skb_reserve(buf, hdr_len); | ||
105 | |||
106 | return buf; | ||
107 | } | ||
108 | |||
109 | static struct sk_buff *tipc_get_err_tlv(char *str) | ||
110 | { | ||
111 | int str_len = strlen(str) + 1; | ||
112 | struct sk_buff *buf; | ||
113 | |||
114 | buf = tipc_tlv_alloc(TLV_SPACE(str_len)); | ||
115 | if (buf) | ||
116 | tipc_add_tlv(buf, TIPC_TLV_ERROR_STRING, str, str_len); | ||
117 | |||
118 | return buf; | ||
119 | } | ||
120 | |||
121 | static int __tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, | ||
122 | struct tipc_nl_compat_msg *msg, | ||
123 | struct sk_buff *arg) | ||
124 | { | ||
125 | int len = 0; | ||
126 | int err; | ||
127 | struct sk_buff *buf; | ||
128 | struct nlmsghdr *nlmsg; | ||
129 | struct netlink_callback cb; | ||
130 | |||
131 | memset(&cb, 0, sizeof(cb)); | ||
132 | cb.nlh = (struct nlmsghdr *)arg->data; | ||
133 | cb.skb = arg; | ||
134 | |||
135 | buf = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
136 | if (!buf) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | buf->sk = msg->dst_sk; | ||
140 | |||
141 | do { | ||
142 | int rem; | ||
143 | |||
144 | len = (*cmd->dumpit)(buf, &cb); | ||
145 | |||
146 | nlmsg_for_each_msg(nlmsg, nlmsg_hdr(buf), len, rem) { | ||
147 | struct nlattr **attrs; | ||
148 | |||
149 | err = tipc_nlmsg_parse(nlmsg, &attrs); | ||
150 | if (err) | ||
151 | goto err_out; | ||
152 | |||
153 | err = (*cmd->format)(msg, attrs); | ||
154 | if (err) | ||
155 | goto err_out; | ||
156 | |||
157 | if (tipc_skb_tailroom(msg->rep) <= 1) { | ||
158 | err = -EMSGSIZE; | ||
159 | goto err_out; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | skb_reset_tail_pointer(buf); | ||
164 | buf->len = 0; | ||
165 | |||
166 | } while (len); | ||
167 | |||
168 | err = 0; | ||
169 | |||
170 | err_out: | ||
171 | kfree_skb(buf); | ||
172 | |||
173 | if (err == -EMSGSIZE) { | ||
174 | /* The legacy API only considered messages filling | ||
175 | * "ULTRA_STRING_MAX_LEN" to be truncated. | ||
176 | */ | ||
177 | if ((TIPC_SKB_MAX - msg->rep->len) <= 1) { | ||
178 | char *tail = skb_tail_pointer(msg->rep); | ||
179 | |||
180 | if (*tail != '\0') | ||
181 | sprintf(tail - sizeof(REPLY_TRUNCATED) - 1, | ||
182 | REPLY_TRUNCATED); | ||
183 | } | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | return err; | ||
189 | } | ||
190 | |||
191 | static int tipc_nl_compat_dumpit(struct tipc_nl_compat_cmd_dump *cmd, | ||
192 | struct tipc_nl_compat_msg *msg) | ||
193 | { | ||
194 | int err; | ||
195 | struct sk_buff *arg; | ||
196 | |||
197 | msg->rep = tipc_tlv_alloc(msg->rep_size); | ||
198 | if (!msg->rep) | ||
199 | return -ENOMEM; | ||
200 | |||
201 | arg = nlmsg_new(0, GFP_KERNEL); | ||
202 | if (!arg) { | ||
203 | kfree_skb(msg->rep); | ||
204 | return -ENOMEM; | ||
205 | } | ||
206 | |||
207 | err = __tipc_nl_compat_dumpit(cmd, msg, arg); | ||
208 | if (err) | ||
209 | kfree_skb(msg->rep); | ||
210 | |||
211 | kfree_skb(arg); | ||
212 | |||
213 | return err; | ||
214 | } | ||
215 | |||
216 | static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg, | ||
217 | struct nlattr **attrs) | ||
218 | { | ||
219 | struct nlattr *bearer[TIPC_NLA_BEARER_MAX + 1]; | ||
220 | |||
221 | nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER], | ||
222 | NULL); | ||
223 | |||
224 | return tipc_add_tlv(msg->rep, TIPC_TLV_BEARER_NAME, | ||
225 | nla_data(bearer[TIPC_NLA_BEARER_NAME]), | ||
226 | nla_len(bearer[TIPC_NLA_BEARER_NAME])); | ||
227 | } | ||
228 | |||
229 | static int tipc_nl_compat_handle(struct tipc_nl_compat_msg *msg) | ||
230 | { | ||
231 | struct tipc_nl_compat_cmd_dump dump; | ||
232 | |||
233 | memset(&dump, 0, sizeof(dump)); | ||
234 | |||
235 | switch (msg->cmd) { | ||
236 | case TIPC_CMD_GET_BEARER_NAMES: | ||
237 | msg->rep_size = MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME); | ||
238 | dump.dumpit = tipc_nl_bearer_dump; | ||
239 | dump.format = tipc_nl_compat_bearer_dump; | ||
240 | return tipc_nl_compat_dumpit(&dump, msg); | ||
241 | } | ||
242 | |||
243 | return -EOPNOTSUPP; | ||
244 | } | ||
245 | |||
246 | static int tipc_nl_compat_recv(struct sk_buff *skb, struct genl_info *info) | ||
247 | { | ||
248 | int err; | ||
249 | int len; | ||
250 | struct tipc_nl_compat_msg msg; | ||
251 | struct nlmsghdr *req_nlh; | ||
252 | struct nlmsghdr *rep_nlh; | ||
253 | struct tipc_genlmsghdr *req_userhdr = info->userhdr; | ||
254 | struct net *net = genl_info_net(info); | ||
255 | |||
256 | memset(&msg, 0, sizeof(msg)); | ||
257 | |||
258 | req_nlh = (struct nlmsghdr *)skb->data; | ||
259 | msg.req = nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN; | ||
260 | msg.cmd = req_userhdr->cmd; | ||
261 | msg.dst_sk = info->dst_sk; | ||
262 | |||
263 | if ((msg.cmd & 0xC000) && (!netlink_net_capable(skb, CAP_NET_ADMIN))) { | ||
264 | msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_NET_ADMIN); | ||
265 | err = -EACCES; | ||
266 | goto send; | ||
267 | } | ||
268 | |||
269 | len = nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN); | ||
270 | if (TLV_GET_LEN(msg.req) && !TLV_OK(msg.req, len)) { | ||
271 | msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); | ||
272 | err = -EOPNOTSUPP; | ||
273 | goto send; | ||
274 | } | ||
275 | |||
276 | err = tipc_nl_compat_handle(&msg); | ||
277 | if (err == -EOPNOTSUPP) | ||
278 | msg.rep = tipc_get_err_tlv(TIPC_CFG_NOT_SUPPORTED); | ||
279 | else if (err == -EINVAL) | ||
280 | msg.rep = tipc_get_err_tlv(TIPC_CFG_TLV_ERROR); | ||
281 | send: | ||
282 | if (!msg.rep) | ||
283 | return err; | ||
284 | |||
285 | len = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); | ||
286 | skb_push(msg.rep, len); | ||
287 | rep_nlh = nlmsg_hdr(msg.rep); | ||
288 | memcpy(rep_nlh, info->nlhdr, len); | ||
289 | rep_nlh->nlmsg_len = msg.rep->len; | ||
290 | genlmsg_unicast(net, msg.rep, NETLINK_CB(skb).portid); | ||
291 | |||
292 | return err; | ||
293 | } | ||
294 | |||
39 | static int handle_cmd(struct sk_buff *skb, struct genl_info *info) | 295 | static int handle_cmd(struct sk_buff *skb, struct genl_info *info) |
40 | { | 296 | { |
41 | struct net *net = genl_info_net(info); | 297 | struct net *net = genl_info_net(info); |
@@ -69,6 +325,22 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) | |||
69 | return 0; | 325 | return 0; |
70 | } | 326 | } |
71 | 327 | ||
328 | /* Temporary function to keep functionality throughout the patchset | ||
329 | * without having to mess with the global variables and other trickery | ||
330 | * of the old API. | ||
331 | */ | ||
332 | static int tipc_nl_compat_tmp_wrap(struct sk_buff *skb, struct genl_info *info) | ||
333 | { | ||
334 | struct tipc_genlmsghdr *req = info->userhdr; | ||
335 | |||
336 | switch (req->cmd) { | ||
337 | case TIPC_CMD_GET_BEARER_NAMES: | ||
338 | return tipc_nl_compat_recv(skb, info); | ||
339 | } | ||
340 | |||
341 | return handle_cmd(skb, info); | ||
342 | } | ||
343 | |||
72 | static struct genl_family tipc_genl_compat_family = { | 344 | static struct genl_family tipc_genl_compat_family = { |
73 | .id = GENL_ID_GENERATE, | 345 | .id = GENL_ID_GENERATE, |
74 | .name = TIPC_GENL_NAME, | 346 | .name = TIPC_GENL_NAME, |
@@ -81,7 +353,7 @@ static struct genl_family tipc_genl_compat_family = { | |||
81 | static struct genl_ops tipc_genl_compat_ops[] = { | 353 | static struct genl_ops tipc_genl_compat_ops[] = { |
82 | { | 354 | { |
83 | .cmd = TIPC_GENL_CMD, | 355 | .cmd = TIPC_GENL_CMD, |
84 | .doit = handle_cmd, | 356 | .doit = tipc_nl_compat_tmp_wrap, |
85 | }, | 357 | }, |
86 | }; | 358 | }; |
87 | 359 | ||