diff options
author | Einar Lueck <elelueck@de.ibm.com> | 2010-12-07 21:57:58 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-12-10 17:28:47 -0500 |
commit | d0ddf30fdd2b98fb547ffa33bb79a7a96ef8d7dd (patch) | |
tree | d4f714f2d40dd78241d4351aa4dffab5f6e364ed /drivers/s390/net | |
parent | c07224005dd3fe746246acadc9be652a588a4d7f (diff) |
qeth: support ipv6 query arp cache for HiperSockets
Function qeth_l3_arp_query now queries for IPv6 addresses, too, if
QETH_QARP_WITH_IPV6 is passed as parameter to the ioctl. HiperSockets
and GuestLAN in HiperSockets mode provide corresponding entries.
Signed-off-by: Einar Lueck <elelueck@de.ibm.com>
Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/s390/net')
-rw-r--r-- | drivers/s390/net/qeth_core_mpc.h | 2 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 215 |
2 files changed, 144 insertions, 73 deletions
diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index e37dd8c4bf4e..07d588867b57 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h | |||
@@ -333,7 +333,7 @@ struct qeth_arp_query_data { | |||
333 | __u16 request_bits; | 333 | __u16 request_bits; |
334 | __u16 reply_bits; | 334 | __u16 reply_bits; |
335 | __u32 no_entries; | 335 | __u32 no_entries; |
336 | char data; | 336 | char data; /* only for replies */ |
337 | } __attribute__((packed)); | 337 | } __attribute__((packed)); |
338 | 338 | ||
339 | /* used as parameter for arp_query reply */ | 339 | /* used as parameter for arp_query reply */ |
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index a1abb37db000..a7590551e574 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "qeth_l3.h" | 31 | #include "qeth_l3.h" |
32 | 32 | ||
33 | |||
33 | static int qeth_l3_set_offline(struct ccwgroup_device *); | 34 | static int qeth_l3_set_offline(struct ccwgroup_device *); |
34 | static int qeth_l3_recover(void *); | 35 | static int qeth_l3_recover(void *); |
35 | static int qeth_l3_stop(struct net_device *); | 36 | static int qeth_l3_stop(struct net_device *); |
@@ -2455,22 +2456,46 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) | |||
2455 | return rc; | 2456 | return rc; |
2456 | } | 2457 | } |
2457 | 2458 | ||
2458 | static void qeth_l3_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo, | 2459 | static __u32 get_arp_entry_size(struct qeth_card *card, |
2459 | struct qeth_arp_query_data *qdata, int entry_size, | 2460 | struct qeth_arp_query_data *qdata, |
2460 | int uentry_size) | 2461 | struct qeth_arp_entrytype *type, __u8 strip_entries) |
2461 | { | 2462 | { |
2462 | char *entry_ptr; | 2463 | __u32 rc; |
2463 | char *uentry_ptr; | 2464 | __u8 is_hsi; |
2464 | int i; | ||
2465 | 2465 | ||
2466 | entry_ptr = (char *)&qdata->data; | 2466 | is_hsi = qdata->reply_bits == 5; |
2467 | uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset); | 2467 | if (type->ip == QETHARP_IP_ADDR_V4) { |
2468 | for (i = 0; i < qdata->no_entries; ++i) { | 2468 | QETH_CARD_TEXT(card, 4, "arpev4"); |
2469 | /* strip off 32 bytes "media specific information" */ | 2469 | if (strip_entries) { |
2470 | memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32); | 2470 | rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) : |
2471 | entry_ptr += entry_size; | 2471 | sizeof(struct qeth_arp_qi_entry7_short); |
2472 | uentry_ptr += uentry_size; | 2472 | } else { |
2473 | rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) : | ||
2474 | sizeof(struct qeth_arp_qi_entry7); | ||
2475 | } | ||
2476 | } else if (type->ip == QETHARP_IP_ADDR_V6) { | ||
2477 | QETH_CARD_TEXT(card, 4, "arpev6"); | ||
2478 | if (strip_entries) { | ||
2479 | rc = is_hsi ? | ||
2480 | sizeof(struct qeth_arp_qi_entry5_short_ipv6) : | ||
2481 | sizeof(struct qeth_arp_qi_entry7_short_ipv6); | ||
2482 | } else { | ||
2483 | rc = is_hsi ? | ||
2484 | sizeof(struct qeth_arp_qi_entry5_ipv6) : | ||
2485 | sizeof(struct qeth_arp_qi_entry7_ipv6); | ||
2486 | } | ||
2487 | } else { | ||
2488 | QETH_CARD_TEXT(card, 4, "arpinv"); | ||
2489 | rc = 0; | ||
2473 | } | 2490 | } |
2491 | |||
2492 | return rc; | ||
2493 | } | ||
2494 | |||
2495 | static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot) | ||
2496 | { | ||
2497 | return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) || | ||
2498 | (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6); | ||
2474 | } | 2499 | } |
2475 | 2500 | ||
2476 | static int qeth_l3_arp_query_cb(struct qeth_card *card, | 2501 | static int qeth_l3_arp_query_cb(struct qeth_card *card, |
@@ -2479,72 +2504,77 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card, | |||
2479 | struct qeth_ipa_cmd *cmd; | 2504 | struct qeth_ipa_cmd *cmd; |
2480 | struct qeth_arp_query_data *qdata; | 2505 | struct qeth_arp_query_data *qdata; |
2481 | struct qeth_arp_query_info *qinfo; | 2506 | struct qeth_arp_query_info *qinfo; |
2482 | int entry_size; | ||
2483 | int uentry_size; | ||
2484 | int i; | 2507 | int i; |
2508 | int e; | ||
2509 | int entrybytes_done; | ||
2510 | int stripped_bytes; | ||
2511 | __u8 do_strip_entries; | ||
2485 | 2512 | ||
2486 | QETH_CARD_TEXT(card, 4, "arpquecb"); | 2513 | QETH_CARD_TEXT(card, 3, "arpquecb"); |
2487 | 2514 | ||
2488 | qinfo = (struct qeth_arp_query_info *) reply->param; | 2515 | qinfo = (struct qeth_arp_query_info *) reply->param; |
2489 | cmd = (struct qeth_ipa_cmd *) data; | 2516 | cmd = (struct qeth_ipa_cmd *) data; |
2517 | QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version); | ||
2490 | if (cmd->hdr.return_code) { | 2518 | if (cmd->hdr.return_code) { |
2491 | QETH_CARD_TEXT_(card, 4, "qaer1%i", cmd->hdr.return_code); | 2519 | QETH_CARD_TEXT(card, 4, "arpcberr"); |
2520 | QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); | ||
2492 | return 0; | 2521 | return 0; |
2493 | } | 2522 | } |
2494 | if (cmd->data.setassparms.hdr.return_code) { | 2523 | if (cmd->data.setassparms.hdr.return_code) { |
2495 | cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; | 2524 | cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; |
2496 | QETH_CARD_TEXT_(card, 4, "qaer2%i", cmd->hdr.return_code); | 2525 | QETH_CARD_TEXT(card, 4, "setaperr"); |
2526 | QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); | ||
2497 | return 0; | 2527 | return 0; |
2498 | } | 2528 | } |
2499 | qdata = &cmd->data.setassparms.data.query_arp; | 2529 | qdata = &cmd->data.setassparms.data.query_arp; |
2500 | switch (qdata->reply_bits) { | ||
2501 | case 5: | ||
2502 | uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5); | ||
2503 | if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) | ||
2504 | uentry_size = sizeof(struct qeth_arp_qi_entry5_short); | ||
2505 | break; | ||
2506 | case 7: | ||
2507 | /* fall through to default */ | ||
2508 | default: | ||
2509 | /* tr is the same as eth -> entry7 */ | ||
2510 | uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7); | ||
2511 | if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) | ||
2512 | uentry_size = sizeof(struct qeth_arp_qi_entry7_short); | ||
2513 | break; | ||
2514 | } | ||
2515 | /* check if there is enough room in userspace */ | ||
2516 | if ((qinfo->udata_len - qinfo->udata_offset) < | ||
2517 | qdata->no_entries * uentry_size){ | ||
2518 | QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); | ||
2519 | cmd->hdr.return_code = -ENOMEM; | ||
2520 | goto out_error; | ||
2521 | } | ||
2522 | QETH_CARD_TEXT_(card, 4, "anore%i", | ||
2523 | cmd->data.setassparms.hdr.number_of_replies); | ||
2524 | QETH_CARD_TEXT_(card, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no); | ||
2525 | QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); | 2530 | QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); |
2526 | 2531 | ||
2527 | if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) { | 2532 | do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0; |
2528 | /* strip off "media specific information" */ | 2533 | stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0; |
2529 | qeth_l3_copy_arp_entries_stripped(qinfo, qdata, entry_size, | 2534 | entrybytes_done = 0; |
2530 | uentry_size); | 2535 | for (e = 0; e < qdata->no_entries; ++e) { |
2531 | } else | 2536 | char *cur_entry; |
2532 | /*copy entries to user buffer*/ | 2537 | __u32 esize; |
2533 | memcpy(qinfo->udata + qinfo->udata_offset, | 2538 | struct qeth_arp_entrytype *etype; |
2534 | (char *)&qdata->data, qdata->no_entries*uentry_size); | 2539 | |
2540 | cur_entry = &qdata->data + entrybytes_done; | ||
2541 | etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type; | ||
2542 | if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) { | ||
2543 | QETH_CARD_TEXT(card, 4, "pmis"); | ||
2544 | QETH_CARD_TEXT_(card, 4, "%i", etype->ip); | ||
2545 | break; | ||
2546 | } | ||
2547 | esize = get_arp_entry_size(card, qdata, etype, | ||
2548 | do_strip_entries); | ||
2549 | QETH_CARD_TEXT_(card, 5, "esz%i", esize); | ||
2550 | if (!esize) | ||
2551 | break; | ||
2552 | |||
2553 | if ((qinfo->udata_len - qinfo->udata_offset) < esize) { | ||
2554 | QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOMEM); | ||
2555 | cmd->hdr.return_code = -ENOMEM; | ||
2556 | goto out_error; | ||
2557 | } | ||
2535 | 2558 | ||
2536 | qinfo->no_entries += qdata->no_entries; | 2559 | memcpy(qinfo->udata + qinfo->udata_offset, |
2537 | qinfo->udata_offset += (qdata->no_entries*uentry_size); | 2560 | &qdata->data + entrybytes_done + stripped_bytes, |
2561 | esize); | ||
2562 | entrybytes_done += esize + stripped_bytes; | ||
2563 | qinfo->udata_offset += esize; | ||
2564 | ++qinfo->no_entries; | ||
2565 | } | ||
2538 | /* check if all replies received ... */ | 2566 | /* check if all replies received ... */ |
2539 | if (cmd->data.setassparms.hdr.seq_no < | 2567 | if (cmd->data.setassparms.hdr.seq_no < |
2540 | cmd->data.setassparms.hdr.number_of_replies) | 2568 | cmd->data.setassparms.hdr.number_of_replies) |
2541 | return 1; | 2569 | return 1; |
2570 | QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries); | ||
2542 | memcpy(qinfo->udata, &qinfo->no_entries, 4); | 2571 | memcpy(qinfo->udata, &qinfo->no_entries, 4); |
2543 | /* keep STRIP_ENTRIES flag so the user program can distinguish | 2572 | /* keep STRIP_ENTRIES flag so the user program can distinguish |
2544 | * stripped entries from normal ones */ | 2573 | * stripped entries from normal ones */ |
2545 | if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) | 2574 | if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) |
2546 | qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; | 2575 | qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; |
2547 | memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); | 2576 | memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); |
2577 | QETH_CARD_TEXT_(card, 4, "rc%i", 0); | ||
2548 | return 0; | 2578 | return 0; |
2549 | out_error: | 2579 | out_error: |
2550 | i = 0; | 2580 | i = 0; |
@@ -2567,45 +2597,86 @@ static int qeth_l3_send_ipa_arp_cmd(struct qeth_card *card, | |||
2567 | reply_cb, reply_param); | 2597 | reply_cb, reply_param); |
2568 | } | 2598 | } |
2569 | 2599 | ||
2570 | static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) | 2600 | static int qeth_l3_query_arp_cache_info(struct qeth_card *card, |
2601 | enum qeth_prot_versions prot, | ||
2602 | struct qeth_arp_query_info *qinfo) | ||
2571 | { | 2603 | { |
2572 | struct qeth_cmd_buffer *iob; | 2604 | struct qeth_cmd_buffer *iob; |
2573 | struct qeth_arp_query_info qinfo = {0, }; | 2605 | struct qeth_ipa_cmd *cmd; |
2574 | int tmp; | 2606 | int tmp; |
2575 | int rc; | 2607 | int rc; |
2576 | 2608 | ||
2609 | QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); | ||
2610 | |||
2611 | iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, | ||
2612 | IPA_CMD_ASS_ARP_QUERY_INFO, | ||
2613 | sizeof(struct qeth_arp_query_data) - sizeof(char), | ||
2614 | prot); | ||
2615 | cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); | ||
2616 | cmd->data.setassparms.data.query_arp.request_bits = 0x000F; | ||
2617 | cmd->data.setassparms.data.query_arp.reply_bits = 0; | ||
2618 | cmd->data.setassparms.data.query_arp.no_entries = 0; | ||
2619 | rc = qeth_l3_send_ipa_arp_cmd(card, iob, | ||
2620 | QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, | ||
2621 | qeth_l3_arp_query_cb, (void *)qinfo); | ||
2622 | if (rc) { | ||
2623 | tmp = rc; | ||
2624 | QETH_DBF_MESSAGE(2, | ||
2625 | "Error while querying ARP cache on %s: %s " | ||
2626 | "(0x%x/%d)\n", QETH_CARD_IFNAME(card), | ||
2627 | qeth_l3_arp_get_error_cause(&rc), tmp, tmp); | ||
2628 | } | ||
2629 | |||
2630 | return rc; | ||
2631 | } | ||
2632 | |||
2633 | static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) | ||
2634 | { | ||
2635 | struct qeth_arp_query_info qinfo = {0, }; | ||
2636 | int rc; | ||
2637 | |||
2577 | QETH_CARD_TEXT(card, 3, "arpquery"); | 2638 | QETH_CARD_TEXT(card, 3, "arpquery"); |
2578 | 2639 | ||
2579 | if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ | 2640 | if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ |
2580 | IPA_ARP_PROCESSING)) { | 2641 | IPA_ARP_PROCESSING)) { |
2581 | return -EOPNOTSUPP; | 2642 | QETH_CARD_TEXT(card, 3, "arpqnsup"); |
2643 | rc = -EOPNOTSUPP; | ||
2644 | goto out; | ||
2582 | } | 2645 | } |
2583 | /* get size of userspace buffer and mask_bits -> 6 bytes */ | 2646 | /* get size of userspace buffer and mask_bits -> 6 bytes */ |
2584 | if (copy_from_user(&qinfo, udata, 6)) | 2647 | if (copy_from_user(&qinfo, udata, 6)) { |
2585 | return -EFAULT; | 2648 | rc = -EFAULT; |
2649 | goto out; | ||
2650 | } | ||
2586 | qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); | 2651 | qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); |
2587 | if (!qinfo.udata) | 2652 | if (!qinfo.udata) { |
2588 | return -ENOMEM; | 2653 | rc = -ENOMEM; |
2654 | goto out; | ||
2655 | } | ||
2589 | qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; | 2656 | qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; |
2590 | iob = qeth_l3_get_setassparms_cmd(card, IPA_ARP_PROCESSING, | 2657 | rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo); |
2591 | IPA_CMD_ASS_ARP_QUERY_INFO, | ||
2592 | sizeof(int), QETH_PROT_IPV4); | ||
2593 | |||
2594 | rc = qeth_l3_send_ipa_arp_cmd(card, iob, | ||
2595 | QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN, | ||
2596 | qeth_l3_arp_query_cb, (void *)&qinfo); | ||
2597 | if (rc) { | 2658 | if (rc) { |
2598 | tmp = rc; | ||
2599 | QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s " | ||
2600 | "(0x%x/%d)\n", QETH_CARD_IFNAME(card), | ||
2601 | qeth_l3_arp_get_error_cause(&rc), tmp, tmp); | ||
2602 | if (copy_to_user(udata, qinfo.udata, 4)) | 2659 | if (copy_to_user(udata, qinfo.udata, 4)) |
2603 | rc = -EFAULT; | 2660 | rc = -EFAULT; |
2661 | goto free_and_out; | ||
2604 | } else { | 2662 | } else { |
2605 | if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) | 2663 | #ifdef CONFIG_QETH_IPV6 |
2664 | if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { | ||
2665 | /* fails in case of GuestLAN QDIO mode */ | ||
2666 | qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, | ||
2667 | &qinfo); | ||
2668 | } | ||
2669 | #endif | ||
2670 | if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { | ||
2671 | QETH_CARD_TEXT(card, 4, "qactf"); | ||
2606 | rc = -EFAULT; | 2672 | rc = -EFAULT; |
2673 | goto free_and_out; | ||
2674 | } | ||
2675 | QETH_CARD_TEXT_(card, 4, "qacts"); | ||
2607 | } | 2676 | } |
2677 | free_and_out: | ||
2608 | kfree(qinfo.udata); | 2678 | kfree(qinfo.udata); |
2679 | out: | ||
2609 | return rc; | 2680 | return rc; |
2610 | } | 2681 | } |
2611 | 2682 | ||