1 | /* |
2 | * Copyright (c) 2014 Chelsio, Inc. All rights reserved. |
3 | * Copyright (c) 2014 Intel Corporation. All rights reserved. |
4 | * |
5 | * This software is available to you under a choice of one of two |
6 | * licenses. You may choose to be licensed under the terms of the GNU |
7 | * General Public License (GPL) Version 2, available from the file |
8 | * COPYING in the main directory of this source tree, or the |
9 | * OpenIB.org BSD license below: |
10 | * |
11 | * Redistribution and use in source and binary forms, with or |
12 | * without modification, are permitted provided that the following |
13 | * conditions are met: |
14 | * |
15 | * - Redistributions of source code must retain the above |
16 | * copyright notice, this list of conditions and the following |
17 | * disclaimer. |
18 | * |
19 | * - Redistributions in binary form must reproduce the above |
20 | * copyright notice, this list of conditions and the following |
21 | * disclaimer in the documentation and/or other materials |
22 | * provided with the distribution. |
23 | * |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
31 | * SOFTWARE. |
32 | */ |
33 | |
34 | #include "iwpm_util.h" |
35 | |
36 | #define IWPM_MAPINFO_HASH_SIZE 512 |
37 | #define IWPM_MAPINFO_HASH_MASK (IWPM_MAPINFO_HASH_SIZE - 1) |
38 | #define IWPM_REMINFO_HASH_SIZE 64 |
39 | #define IWPM_REMINFO_HASH_MASK (IWPM_REMINFO_HASH_SIZE - 1) |
40 | #define IWPM_MSG_SIZE 512 |
41 | |
42 | static LIST_HEAD(iwpm_nlmsg_req_list); |
43 | static DEFINE_SPINLOCK(iwpm_nlmsg_req_lock); |
44 | |
45 | static struct hlist_head *iwpm_hash_bucket; |
46 | static DEFINE_SPINLOCK(iwpm_mapinfo_lock); |
47 | |
48 | static struct hlist_head *iwpm_reminfo_bucket; |
49 | static DEFINE_SPINLOCK(iwpm_reminfo_lock); |
50 | |
51 | static struct iwpm_admin_data iwpm_admin; |
52 | |
53 | /** |
54 | * iwpm_init - Allocate resources for the iwarp port mapper |
55 | * @nl_client: The index of the netlink client |
56 | * |
57 | * Should be called when network interface goes up. |
58 | */ |
59 | int iwpm_init(u8 nl_client) |
60 | { |
61 | iwpm_hash_bucket = kcalloc(IWPM_MAPINFO_HASH_SIZE, |
62 | size: sizeof(struct hlist_head), GFP_KERNEL); |
63 | if (!iwpm_hash_bucket) |
64 | return -ENOMEM; |
65 | |
66 | iwpm_reminfo_bucket = kcalloc(IWPM_REMINFO_HASH_SIZE, |
67 | size: sizeof(struct hlist_head), GFP_KERNEL); |
68 | if (!iwpm_reminfo_bucket) { |
69 | kfree(objp: iwpm_hash_bucket); |
70 | return -ENOMEM; |
71 | } |
72 | |
73 | iwpm_set_registration(nl_client, IWPM_REG_UNDEF); |
74 | pr_debug("%s: Mapinfo and reminfo tables are created\n" , __func__); |
75 | return 0; |
76 | } |
77 | |
78 | static void free_hash_bucket(void); |
79 | static void free_reminfo_bucket(void); |
80 | |
81 | /** |
82 | * iwpm_exit - Deallocate resources for the iwarp port mapper |
83 | * @nl_client: The index of the netlink client |
84 | * |
85 | * Should be called when network interface goes down. |
86 | */ |
87 | int iwpm_exit(u8 nl_client) |
88 | { |
89 | free_hash_bucket(); |
90 | free_reminfo_bucket(); |
91 | pr_debug("%s: Resources are destroyed\n" , __func__); |
92 | iwpm_set_registration(nl_client, IWPM_REG_UNDEF); |
93 | return 0; |
94 | } |
95 | |
96 | static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage *, |
97 | struct sockaddr_storage *); |
98 | |
99 | /** |
100 | * iwpm_create_mapinfo - Store local and mapped IPv4/IPv6 address |
101 | * info in a hash table |
102 | * @local_sockaddr: Local ip/tcp address |
103 | * @mapped_sockaddr: Mapped local ip/tcp address |
104 | * @nl_client: The index of the netlink client |
105 | * @map_flags: IWPM mapping flags |
106 | */ |
107 | int iwpm_create_mapinfo(struct sockaddr_storage *local_sockaddr, |
108 | struct sockaddr_storage *mapped_sockaddr, |
109 | u8 nl_client, u32 map_flags) |
110 | { |
111 | struct hlist_head *hash_bucket_head = NULL; |
112 | struct iwpm_mapping_info *map_info; |
113 | unsigned long flags; |
114 | int ret = -EINVAL; |
115 | |
116 | map_info = kzalloc(size: sizeof(struct iwpm_mapping_info), GFP_KERNEL); |
117 | if (!map_info) |
118 | return -ENOMEM; |
119 | |
120 | memcpy(&map_info->local_sockaddr, local_sockaddr, |
121 | sizeof(struct sockaddr_storage)); |
122 | memcpy(&map_info->mapped_sockaddr, mapped_sockaddr, |
123 | sizeof(struct sockaddr_storage)); |
124 | map_info->nl_client = nl_client; |
125 | map_info->map_flags = map_flags; |
126 | |
127 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
128 | if (iwpm_hash_bucket) { |
129 | hash_bucket_head = get_mapinfo_hash_bucket( |
130 | &map_info->local_sockaddr, |
131 | &map_info->mapped_sockaddr); |
132 | if (hash_bucket_head) { |
133 | hlist_add_head(n: &map_info->hlist_node, h: hash_bucket_head); |
134 | ret = 0; |
135 | } |
136 | } |
137 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, flags); |
138 | |
139 | if (!hash_bucket_head) |
140 | kfree(objp: map_info); |
141 | return ret; |
142 | } |
143 | |
144 | /** |
145 | * iwpm_remove_mapinfo - Remove local and mapped IPv4/IPv6 address |
146 | * info from the hash table |
147 | * @local_sockaddr: Local ip/tcp address |
148 | * @mapped_local_addr: Mapped local ip/tcp address |
149 | * |
150 | * Returns err code if mapping info is not found in the hash table, |
151 | * otherwise returns 0 |
152 | */ |
153 | int iwpm_remove_mapinfo(struct sockaddr_storage *local_sockaddr, |
154 | struct sockaddr_storage *mapped_local_addr) |
155 | { |
156 | struct hlist_node *tmp_hlist_node; |
157 | struct hlist_head *hash_bucket_head; |
158 | struct iwpm_mapping_info *map_info = NULL; |
159 | unsigned long flags; |
160 | int ret = -EINVAL; |
161 | |
162 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
163 | if (iwpm_hash_bucket) { |
164 | hash_bucket_head = get_mapinfo_hash_bucket( |
165 | local_sockaddr, |
166 | mapped_local_addr); |
167 | if (!hash_bucket_head) |
168 | goto remove_mapinfo_exit; |
169 | |
170 | hlist_for_each_entry_safe(map_info, tmp_hlist_node, |
171 | hash_bucket_head, hlist_node) { |
172 | |
173 | if (!iwpm_compare_sockaddr(a_sockaddr: &map_info->mapped_sockaddr, |
174 | b_sockaddr: mapped_local_addr)) { |
175 | |
176 | hlist_del_init(n: &map_info->hlist_node); |
177 | kfree(objp: map_info); |
178 | ret = 0; |
179 | break; |
180 | } |
181 | } |
182 | } |
183 | remove_mapinfo_exit: |
184 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, flags); |
185 | return ret; |
186 | } |
187 | |
188 | static void free_hash_bucket(void) |
189 | { |
190 | struct hlist_node *tmp_hlist_node; |
191 | struct iwpm_mapping_info *map_info; |
192 | unsigned long flags; |
193 | int i; |
194 | |
195 | /* remove all the mapinfo data from the list */ |
196 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
197 | for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) { |
198 | hlist_for_each_entry_safe(map_info, tmp_hlist_node, |
199 | &iwpm_hash_bucket[i], hlist_node) { |
200 | |
201 | hlist_del_init(n: &map_info->hlist_node); |
202 | kfree(objp: map_info); |
203 | } |
204 | } |
205 | /* free the hash list */ |
206 | kfree(objp: iwpm_hash_bucket); |
207 | iwpm_hash_bucket = NULL; |
208 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, flags); |
209 | } |
210 | |
211 | static void free_reminfo_bucket(void) |
212 | { |
213 | struct hlist_node *tmp_hlist_node; |
214 | struct iwpm_remote_info *rem_info; |
215 | unsigned long flags; |
216 | int i; |
217 | |
218 | /* remove all the remote info from the list */ |
219 | spin_lock_irqsave(&iwpm_reminfo_lock, flags); |
220 | for (i = 0; i < IWPM_REMINFO_HASH_SIZE; i++) { |
221 | hlist_for_each_entry_safe(rem_info, tmp_hlist_node, |
222 | &iwpm_reminfo_bucket[i], hlist_node) { |
223 | |
224 | hlist_del_init(n: &rem_info->hlist_node); |
225 | kfree(objp: rem_info); |
226 | } |
227 | } |
228 | /* free the hash list */ |
229 | kfree(objp: iwpm_reminfo_bucket); |
230 | iwpm_reminfo_bucket = NULL; |
231 | spin_unlock_irqrestore(lock: &iwpm_reminfo_lock, flags); |
232 | } |
233 | |
234 | static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage *, |
235 | struct sockaddr_storage *); |
236 | |
237 | void iwpm_add_remote_info(struct iwpm_remote_info *rem_info) |
238 | { |
239 | struct hlist_head *hash_bucket_head; |
240 | unsigned long flags; |
241 | |
242 | spin_lock_irqsave(&iwpm_reminfo_lock, flags); |
243 | if (iwpm_reminfo_bucket) { |
244 | hash_bucket_head = get_reminfo_hash_bucket( |
245 | &rem_info->mapped_loc_sockaddr, |
246 | &rem_info->mapped_rem_sockaddr); |
247 | if (hash_bucket_head) |
248 | hlist_add_head(n: &rem_info->hlist_node, h: hash_bucket_head); |
249 | } |
250 | spin_unlock_irqrestore(lock: &iwpm_reminfo_lock, flags); |
251 | } |
252 | |
253 | /** |
254 | * iwpm_get_remote_info - Get the remote connecting peer address info |
255 | * |
256 | * @mapped_loc_addr: Mapped local address of the listening peer |
257 | * @mapped_rem_addr: Mapped remote address of the connecting peer |
258 | * @remote_addr: To store the remote address of the connecting peer |
259 | * @nl_client: The index of the netlink client |
260 | * |
261 | * The remote address info is retrieved and provided to the client in |
262 | * the remote_addr. After that it is removed from the hash table |
263 | */ |
264 | int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, |
265 | struct sockaddr_storage *mapped_rem_addr, |
266 | struct sockaddr_storage *remote_addr, |
267 | u8 nl_client) |
268 | { |
269 | struct hlist_node *tmp_hlist_node; |
270 | struct hlist_head *hash_bucket_head; |
271 | struct iwpm_remote_info *rem_info = NULL; |
272 | unsigned long flags; |
273 | int ret = -EINVAL; |
274 | |
275 | spin_lock_irqsave(&iwpm_reminfo_lock, flags); |
276 | if (iwpm_reminfo_bucket) { |
277 | hash_bucket_head = get_reminfo_hash_bucket( |
278 | mapped_loc_addr, |
279 | mapped_rem_addr); |
280 | if (!hash_bucket_head) |
281 | goto get_remote_info_exit; |
282 | hlist_for_each_entry_safe(rem_info, tmp_hlist_node, |
283 | hash_bucket_head, hlist_node) { |
284 | |
285 | if (!iwpm_compare_sockaddr(a_sockaddr: &rem_info->mapped_loc_sockaddr, |
286 | b_sockaddr: mapped_loc_addr) && |
287 | !iwpm_compare_sockaddr(a_sockaddr: &rem_info->mapped_rem_sockaddr, |
288 | b_sockaddr: mapped_rem_addr)) { |
289 | |
290 | memcpy(remote_addr, &rem_info->remote_sockaddr, |
291 | sizeof(struct sockaddr_storage)); |
292 | iwpm_print_sockaddr(sockaddr: remote_addr, |
293 | msg: "get_remote_info: Remote sockaddr:" ); |
294 | |
295 | hlist_del_init(n: &rem_info->hlist_node); |
296 | kfree(objp: rem_info); |
297 | ret = 0; |
298 | break; |
299 | } |
300 | } |
301 | } |
302 | get_remote_info_exit: |
303 | spin_unlock_irqrestore(lock: &iwpm_reminfo_lock, flags); |
304 | return ret; |
305 | } |
306 | |
307 | struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, |
308 | u8 nl_client, gfp_t gfp) |
309 | { |
310 | struct iwpm_nlmsg_request *nlmsg_request; |
311 | unsigned long flags; |
312 | |
313 | nlmsg_request = kzalloc(size: sizeof(struct iwpm_nlmsg_request), flags: gfp); |
314 | if (!nlmsg_request) |
315 | return NULL; |
316 | |
317 | spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags); |
318 | list_add_tail(new: &nlmsg_request->inprocess_list, head: &iwpm_nlmsg_req_list); |
319 | spin_unlock_irqrestore(lock: &iwpm_nlmsg_req_lock, flags); |
320 | |
321 | kref_init(kref: &nlmsg_request->kref); |
322 | kref_get(kref: &nlmsg_request->kref); |
323 | nlmsg_request->nlmsg_seq = nlmsg_seq; |
324 | nlmsg_request->nl_client = nl_client; |
325 | nlmsg_request->request_done = 0; |
326 | nlmsg_request->err_code = 0; |
327 | sema_init(sem: &nlmsg_request->sem, val: 1); |
328 | down(sem: &nlmsg_request->sem); |
329 | return nlmsg_request; |
330 | } |
331 | |
332 | void iwpm_free_nlmsg_request(struct kref *kref) |
333 | { |
334 | struct iwpm_nlmsg_request *nlmsg_request; |
335 | unsigned long flags; |
336 | |
337 | nlmsg_request = container_of(kref, struct iwpm_nlmsg_request, kref); |
338 | |
339 | spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags); |
340 | list_del_init(entry: &nlmsg_request->inprocess_list); |
341 | spin_unlock_irqrestore(lock: &iwpm_nlmsg_req_lock, flags); |
342 | |
343 | if (!nlmsg_request->request_done) |
344 | pr_debug("%s Freeing incomplete nlmsg request (seq = %u).\n" , |
345 | __func__, nlmsg_request->nlmsg_seq); |
346 | kfree(objp: nlmsg_request); |
347 | } |
348 | |
349 | struct iwpm_nlmsg_request *iwpm_find_nlmsg_request(__u32 echo_seq) |
350 | { |
351 | struct iwpm_nlmsg_request *nlmsg_request; |
352 | struct iwpm_nlmsg_request *found_request = NULL; |
353 | unsigned long flags; |
354 | |
355 | spin_lock_irqsave(&iwpm_nlmsg_req_lock, flags); |
356 | list_for_each_entry(nlmsg_request, &iwpm_nlmsg_req_list, |
357 | inprocess_list) { |
358 | if (nlmsg_request->nlmsg_seq == echo_seq) { |
359 | found_request = nlmsg_request; |
360 | kref_get(kref: &nlmsg_request->kref); |
361 | break; |
362 | } |
363 | } |
364 | spin_unlock_irqrestore(lock: &iwpm_nlmsg_req_lock, flags); |
365 | return found_request; |
366 | } |
367 | |
368 | int iwpm_wait_complete_req(struct iwpm_nlmsg_request *nlmsg_request) |
369 | { |
370 | int ret; |
371 | |
372 | ret = down_timeout(sem: &nlmsg_request->sem, IWPM_NL_TIMEOUT); |
373 | if (ret) { |
374 | ret = -EINVAL; |
375 | pr_info("%s: Timeout %d sec for netlink request (seq = %u)\n" , |
376 | __func__, (IWPM_NL_TIMEOUT/HZ), nlmsg_request->nlmsg_seq); |
377 | } else { |
378 | ret = nlmsg_request->err_code; |
379 | } |
380 | kref_put(kref: &nlmsg_request->kref, release: iwpm_free_nlmsg_request); |
381 | return ret; |
382 | } |
383 | |
384 | int iwpm_get_nlmsg_seq(void) |
385 | { |
386 | return atomic_inc_return(v: &iwpm_admin.nlmsg_seq); |
387 | } |
388 | |
389 | /* valid client */ |
390 | u32 iwpm_get_registration(u8 nl_client) |
391 | { |
392 | return iwpm_admin.reg_list[nl_client]; |
393 | } |
394 | |
395 | /* valid client */ |
396 | void iwpm_set_registration(u8 nl_client, u32 reg) |
397 | { |
398 | iwpm_admin.reg_list[nl_client] = reg; |
399 | } |
400 | |
401 | /* valid client */ |
402 | u32 iwpm_check_registration(u8 nl_client, u32 reg) |
403 | { |
404 | return (iwpm_get_registration(nl_client) & reg); |
405 | } |
406 | |
407 | int iwpm_compare_sockaddr(struct sockaddr_storage *a_sockaddr, |
408 | struct sockaddr_storage *b_sockaddr) |
409 | { |
410 | if (a_sockaddr->ss_family != b_sockaddr->ss_family) |
411 | return 1; |
412 | if (a_sockaddr->ss_family == AF_INET) { |
413 | struct sockaddr_in *a4_sockaddr = |
414 | (struct sockaddr_in *)a_sockaddr; |
415 | struct sockaddr_in *b4_sockaddr = |
416 | (struct sockaddr_in *)b_sockaddr; |
417 | if (!memcmp(p: &a4_sockaddr->sin_addr, |
418 | q: &b4_sockaddr->sin_addr, size: sizeof(struct in_addr)) |
419 | && a4_sockaddr->sin_port == b4_sockaddr->sin_port) |
420 | return 0; |
421 | |
422 | } else if (a_sockaddr->ss_family == AF_INET6) { |
423 | struct sockaddr_in6 *a6_sockaddr = |
424 | (struct sockaddr_in6 *)a_sockaddr; |
425 | struct sockaddr_in6 *b6_sockaddr = |
426 | (struct sockaddr_in6 *)b_sockaddr; |
427 | if (!memcmp(p: &a6_sockaddr->sin6_addr, |
428 | q: &b6_sockaddr->sin6_addr, size: sizeof(struct in6_addr)) |
429 | && a6_sockaddr->sin6_port == b6_sockaddr->sin6_port) |
430 | return 0; |
431 | |
432 | } else { |
433 | pr_err("%s: Invalid sockaddr family\n" , __func__); |
434 | } |
435 | return 1; |
436 | } |
437 | |
438 | struct sk_buff *iwpm_create_nlmsg(u32 nl_op, struct nlmsghdr **nlh, |
439 | int nl_client) |
440 | { |
441 | struct sk_buff *skb = NULL; |
442 | |
443 | skb = dev_alloc_skb(IWPM_MSG_SIZE); |
444 | if (!skb) |
445 | goto create_nlmsg_exit; |
446 | |
447 | if (!(ibnl_put_msg(skb, nlh, seq: 0, len: 0, client: nl_client, op: nl_op, |
448 | NLM_F_REQUEST))) { |
449 | pr_warn("%s: Unable to put the nlmsg header\n" , __func__); |
450 | dev_kfree_skb(skb); |
451 | skb = NULL; |
452 | } |
453 | create_nlmsg_exit: |
454 | return skb; |
455 | } |
456 | |
457 | int iwpm_parse_nlmsg(struct netlink_callback *cb, int policy_max, |
458 | const struct nla_policy *nlmsg_policy, |
459 | struct nlattr *nltb[], const char *msg_type) |
460 | { |
461 | int nlh_len = 0; |
462 | int ret; |
463 | const char *err_str = "" ; |
464 | |
465 | ret = nlmsg_validate_deprecated(nlh: cb->nlh, hdrlen: nlh_len, maxtype: policy_max - 1, |
466 | policy: nlmsg_policy, NULL); |
467 | if (ret) { |
468 | err_str = "Invalid attribute" ; |
469 | goto parse_nlmsg_error; |
470 | } |
471 | ret = nlmsg_parse_deprecated(nlh: cb->nlh, hdrlen: nlh_len, tb: nltb, maxtype: policy_max - 1, |
472 | policy: nlmsg_policy, NULL); |
473 | if (ret) { |
474 | err_str = "Unable to parse the nlmsg" ; |
475 | goto parse_nlmsg_error; |
476 | } |
477 | ret = iwpm_validate_nlmsg_attr(nltb, nla_count: policy_max); |
478 | if (ret) { |
479 | err_str = "Invalid NULL attribute" ; |
480 | goto parse_nlmsg_error; |
481 | } |
482 | return 0; |
483 | parse_nlmsg_error: |
484 | pr_warn("%s: %s (msg type %s ret = %d)\n" , |
485 | __func__, err_str, msg_type, ret); |
486 | return ret; |
487 | } |
488 | |
489 | void iwpm_print_sockaddr(struct sockaddr_storage *sockaddr, char *msg) |
490 | { |
491 | struct sockaddr_in6 *sockaddr_v6; |
492 | struct sockaddr_in *sockaddr_v4; |
493 | |
494 | switch (sockaddr->ss_family) { |
495 | case AF_INET: |
496 | sockaddr_v4 = (struct sockaddr_in *)sockaddr; |
497 | pr_debug("%s IPV4 %pI4: %u(0x%04X)\n" , |
498 | msg, &sockaddr_v4->sin_addr, |
499 | ntohs(sockaddr_v4->sin_port), |
500 | ntohs(sockaddr_v4->sin_port)); |
501 | break; |
502 | case AF_INET6: |
503 | sockaddr_v6 = (struct sockaddr_in6 *)sockaddr; |
504 | pr_debug("%s IPV6 %pI6: %u(0x%04X)\n" , |
505 | msg, &sockaddr_v6->sin6_addr, |
506 | ntohs(sockaddr_v6->sin6_port), |
507 | ntohs(sockaddr_v6->sin6_port)); |
508 | break; |
509 | default: |
510 | break; |
511 | } |
512 | } |
513 | |
514 | static u32 iwpm_ipv6_jhash(struct sockaddr_in6 *ipv6_sockaddr) |
515 | { |
516 | u32 ipv6_hash = jhash(key: &ipv6_sockaddr->sin6_addr, length: sizeof(struct in6_addr), initval: 0); |
517 | u32 hash = jhash_2words(a: ipv6_hash, b: (__force u32) ipv6_sockaddr->sin6_port, initval: 0); |
518 | return hash; |
519 | } |
520 | |
521 | static u32 iwpm_ipv4_jhash(struct sockaddr_in *ipv4_sockaddr) |
522 | { |
523 | u32 ipv4_hash = jhash(key: &ipv4_sockaddr->sin_addr, length: sizeof(struct in_addr), initval: 0); |
524 | u32 hash = jhash_2words(a: ipv4_hash, b: (__force u32) ipv4_sockaddr->sin_port, initval: 0); |
525 | return hash; |
526 | } |
527 | |
528 | static int get_hash_bucket(struct sockaddr_storage *a_sockaddr, |
529 | struct sockaddr_storage *b_sockaddr, u32 *hash) |
530 | { |
531 | u32 a_hash, b_hash; |
532 | |
533 | if (a_sockaddr->ss_family == AF_INET) { |
534 | a_hash = iwpm_ipv4_jhash(ipv4_sockaddr: (struct sockaddr_in *) a_sockaddr); |
535 | b_hash = iwpm_ipv4_jhash(ipv4_sockaddr: (struct sockaddr_in *) b_sockaddr); |
536 | |
537 | } else if (a_sockaddr->ss_family == AF_INET6) { |
538 | a_hash = iwpm_ipv6_jhash(ipv6_sockaddr: (struct sockaddr_in6 *) a_sockaddr); |
539 | b_hash = iwpm_ipv6_jhash(ipv6_sockaddr: (struct sockaddr_in6 *) b_sockaddr); |
540 | } else { |
541 | pr_err("%s: Invalid sockaddr family\n" , __func__); |
542 | return -EINVAL; |
543 | } |
544 | |
545 | if (a_hash == b_hash) /* if port mapper isn't available */ |
546 | *hash = a_hash; |
547 | else |
548 | *hash = jhash_2words(a: a_hash, b: b_hash, initval: 0); |
549 | return 0; |
550 | } |
551 | |
552 | static struct hlist_head *get_mapinfo_hash_bucket(struct sockaddr_storage |
553 | *local_sockaddr, struct sockaddr_storage |
554 | *mapped_sockaddr) |
555 | { |
556 | u32 hash; |
557 | int ret; |
558 | |
559 | ret = get_hash_bucket(a_sockaddr: local_sockaddr, b_sockaddr: mapped_sockaddr, hash: &hash); |
560 | if (ret) |
561 | return NULL; |
562 | return &iwpm_hash_bucket[hash & IWPM_MAPINFO_HASH_MASK]; |
563 | } |
564 | |
565 | static struct hlist_head *get_reminfo_hash_bucket(struct sockaddr_storage |
566 | *mapped_loc_sockaddr, struct sockaddr_storage |
567 | *mapped_rem_sockaddr) |
568 | { |
569 | u32 hash; |
570 | int ret; |
571 | |
572 | ret = get_hash_bucket(a_sockaddr: mapped_loc_sockaddr, b_sockaddr: mapped_rem_sockaddr, hash: &hash); |
573 | if (ret) |
574 | return NULL; |
575 | return &iwpm_reminfo_bucket[hash & IWPM_REMINFO_HASH_MASK]; |
576 | } |
577 | |
578 | static int send_mapinfo_num(u32 mapping_num, u8 nl_client, int iwpm_pid) |
579 | { |
580 | struct sk_buff *skb = NULL; |
581 | struct nlmsghdr *nlh; |
582 | u32 msg_seq; |
583 | const char *err_str = "" ; |
584 | int ret = -EINVAL; |
585 | |
586 | skb = iwpm_create_nlmsg(nl_op: RDMA_NL_IWPM_MAPINFO_NUM, nlh: &nlh, nl_client); |
587 | if (!skb) { |
588 | err_str = "Unable to create a nlmsg" ; |
589 | goto mapinfo_num_error; |
590 | } |
591 | nlh->nlmsg_seq = iwpm_get_nlmsg_seq(); |
592 | msg_seq = 0; |
593 | err_str = "Unable to put attribute of mapinfo number nlmsg" ; |
594 | ret = ibnl_put_attr(skb, nlh, len: sizeof(u32), data: &msg_seq, type: IWPM_NLA_MAPINFO_SEQ); |
595 | if (ret) |
596 | goto mapinfo_num_error; |
597 | ret = ibnl_put_attr(skb, nlh, len: sizeof(u32), |
598 | data: &mapping_num, type: IWPM_NLA_MAPINFO_SEND_NUM); |
599 | if (ret) |
600 | goto mapinfo_num_error; |
601 | |
602 | nlmsg_end(skb, nlh); |
603 | |
604 | ret = rdma_nl_unicast(net: &init_net, skb, pid: iwpm_pid); |
605 | if (ret) { |
606 | skb = NULL; |
607 | err_str = "Unable to send a nlmsg" ; |
608 | goto mapinfo_num_error; |
609 | } |
610 | pr_debug("%s: Sent mapping number = %u\n" , __func__, mapping_num); |
611 | return 0; |
612 | mapinfo_num_error: |
613 | pr_info("%s: %s\n" , __func__, err_str); |
614 | dev_kfree_skb(skb); |
615 | return ret; |
616 | } |
617 | |
618 | static int send_nlmsg_done(struct sk_buff *skb, u8 nl_client, int iwpm_pid) |
619 | { |
620 | struct nlmsghdr *nlh = NULL; |
621 | int ret = 0; |
622 | |
623 | if (!skb) |
624 | return ret; |
625 | if (!(ibnl_put_msg(skb, nlh: &nlh, seq: 0, len: 0, client: nl_client, |
626 | op: RDMA_NL_IWPM_MAPINFO, NLM_F_MULTI))) { |
627 | pr_warn("%s Unable to put NLMSG_DONE\n" , __func__); |
628 | dev_kfree_skb(skb); |
629 | return -ENOMEM; |
630 | } |
631 | nlh->nlmsg_type = NLMSG_DONE; |
632 | ret = rdma_nl_unicast(net: &init_net, skb, pid: iwpm_pid); |
633 | if (ret) |
634 | pr_warn("%s Unable to send a nlmsg\n" , __func__); |
635 | return ret; |
636 | } |
637 | |
638 | int iwpm_send_mapinfo(u8 nl_client, int iwpm_pid) |
639 | { |
640 | struct iwpm_mapping_info *map_info; |
641 | struct sk_buff *skb = NULL; |
642 | struct nlmsghdr *nlh; |
643 | int skb_num = 0, mapping_num = 0; |
644 | int i = 0, nlmsg_bytes = 0; |
645 | unsigned long flags; |
646 | const char *err_str = "" ; |
647 | int ret; |
648 | |
649 | skb = dev_alloc_skb(NLMSG_GOODSIZE); |
650 | if (!skb) { |
651 | ret = -ENOMEM; |
652 | err_str = "Unable to allocate skb" ; |
653 | goto send_mapping_info_exit; |
654 | } |
655 | skb_num++; |
656 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
657 | ret = -EINVAL; |
658 | for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) { |
659 | hlist_for_each_entry(map_info, &iwpm_hash_bucket[i], |
660 | hlist_node) { |
661 | if (map_info->nl_client != nl_client) |
662 | continue; |
663 | nlh = NULL; |
664 | if (!(ibnl_put_msg(skb, nlh: &nlh, seq: 0, len: 0, client: nl_client, |
665 | op: RDMA_NL_IWPM_MAPINFO, NLM_F_MULTI))) { |
666 | ret = -ENOMEM; |
667 | err_str = "Unable to put the nlmsg header" ; |
668 | goto send_mapping_info_unlock; |
669 | } |
670 | err_str = "Unable to put attribute of the nlmsg" ; |
671 | ret = ibnl_put_attr(skb, nlh, |
672 | len: sizeof(struct sockaddr_storage), |
673 | data: &map_info->local_sockaddr, |
674 | type: IWPM_NLA_MAPINFO_LOCAL_ADDR); |
675 | if (ret) |
676 | goto send_mapping_info_unlock; |
677 | |
678 | ret = ibnl_put_attr(skb, nlh, |
679 | len: sizeof(struct sockaddr_storage), |
680 | data: &map_info->mapped_sockaddr, |
681 | type: IWPM_NLA_MAPINFO_MAPPED_ADDR); |
682 | if (ret) |
683 | goto send_mapping_info_unlock; |
684 | |
685 | if (iwpm_ulib_version > IWPM_UABI_VERSION_MIN) { |
686 | ret = ibnl_put_attr(skb, nlh, len: sizeof(u32), |
687 | data: &map_info->map_flags, |
688 | type: IWPM_NLA_MAPINFO_FLAGS); |
689 | if (ret) |
690 | goto send_mapping_info_unlock; |
691 | } |
692 | |
693 | nlmsg_end(skb, nlh); |
694 | |
695 | iwpm_print_sockaddr(sockaddr: &map_info->local_sockaddr, |
696 | msg: "send_mapping_info: Local sockaddr:" ); |
697 | iwpm_print_sockaddr(sockaddr: &map_info->mapped_sockaddr, |
698 | msg: "send_mapping_info: Mapped local sockaddr:" ); |
699 | mapping_num++; |
700 | nlmsg_bytes += nlh->nlmsg_len; |
701 | |
702 | /* check if all mappings can fit in one skb */ |
703 | if (NLMSG_GOODSIZE - nlmsg_bytes < nlh->nlmsg_len * 2) { |
704 | /* and leave room for NLMSG_DONE */ |
705 | nlmsg_bytes = 0; |
706 | skb_num++; |
707 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, |
708 | flags); |
709 | /* send the skb */ |
710 | ret = send_nlmsg_done(skb, nl_client, iwpm_pid); |
711 | skb = NULL; |
712 | if (ret) { |
713 | err_str = "Unable to send map info" ; |
714 | goto send_mapping_info_exit; |
715 | } |
716 | if (skb_num == IWPM_MAPINFO_SKB_COUNT) { |
717 | ret = -ENOMEM; |
718 | err_str = "Insufficient skbs for map info" ; |
719 | goto send_mapping_info_exit; |
720 | } |
721 | skb = dev_alloc_skb(NLMSG_GOODSIZE); |
722 | if (!skb) { |
723 | ret = -ENOMEM; |
724 | err_str = "Unable to allocate skb" ; |
725 | goto send_mapping_info_exit; |
726 | } |
727 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
728 | } |
729 | } |
730 | } |
731 | send_mapping_info_unlock: |
732 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, flags); |
733 | send_mapping_info_exit: |
734 | if (ret) { |
735 | pr_warn("%s: %s (ret = %d)\n" , __func__, err_str, ret); |
736 | dev_kfree_skb(skb); |
737 | return ret; |
738 | } |
739 | send_nlmsg_done(skb, nl_client, iwpm_pid); |
740 | return send_mapinfo_num(mapping_num, nl_client, iwpm_pid); |
741 | } |
742 | |
743 | int iwpm_mapinfo_available(void) |
744 | { |
745 | unsigned long flags; |
746 | int full_bucket = 0, i = 0; |
747 | |
748 | spin_lock_irqsave(&iwpm_mapinfo_lock, flags); |
749 | if (iwpm_hash_bucket) { |
750 | for (i = 0; i < IWPM_MAPINFO_HASH_SIZE; i++) { |
751 | if (!hlist_empty(h: &iwpm_hash_bucket[i])) { |
752 | full_bucket = 1; |
753 | break; |
754 | } |
755 | } |
756 | } |
757 | spin_unlock_irqrestore(lock: &iwpm_mapinfo_lock, flags); |
758 | return full_bucket; |
759 | } |
760 | |
761 | int iwpm_send_hello(u8 nl_client, int iwpm_pid, u16 abi_version) |
762 | { |
763 | struct sk_buff *skb = NULL; |
764 | struct nlmsghdr *nlh; |
765 | const char *err_str; |
766 | int ret = -EINVAL; |
767 | |
768 | skb = iwpm_create_nlmsg(nl_op: RDMA_NL_IWPM_HELLO, nlh: &nlh, nl_client); |
769 | if (!skb) { |
770 | err_str = "Unable to create a nlmsg" ; |
771 | goto hello_num_error; |
772 | } |
773 | nlh->nlmsg_seq = iwpm_get_nlmsg_seq(); |
774 | err_str = "Unable to put attribute of abi_version into nlmsg" ; |
775 | ret = ibnl_put_attr(skb, nlh, len: sizeof(u16), data: &abi_version, |
776 | type: IWPM_NLA_HELLO_ABI_VERSION); |
777 | if (ret) |
778 | goto hello_num_error; |
779 | nlmsg_end(skb, nlh); |
780 | |
781 | ret = rdma_nl_unicast(net: &init_net, skb, pid: iwpm_pid); |
782 | if (ret) { |
783 | skb = NULL; |
784 | err_str = "Unable to send a nlmsg" ; |
785 | goto hello_num_error; |
786 | } |
787 | pr_debug("%s: Sent hello abi_version = %u\n" , __func__, abi_version); |
788 | return 0; |
789 | hello_num_error: |
790 | pr_info("%s: %s\n" , __func__, err_str); |
791 | dev_kfree_skb(skb); |
792 | return ret; |
793 | } |
794 | |