162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2007, 2009 462306a36Sopenharmony_ci * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, 562306a36Sopenharmony_ci * Frank Pavlic <fpavlic@de.ibm.com>, 662306a36Sopenharmony_ci * Thomas Spatzier <tspat@de.ibm.com>, 762306a36Sopenharmony_ci * Frank Blaschka <frank.blaschka@de.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define KMSG_COMPONENT "qeth" 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/moduleparam.h> 1562306a36Sopenharmony_ci#include <linux/bitops.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/etherdevice.h> 2062306a36Sopenharmony_ci#include <linux/ip.h> 2162306a36Sopenharmony_ci#include <linux/in.h> 2262306a36Sopenharmony_ci#include <linux/inet.h> 2362306a36Sopenharmony_ci#include <linux/ipv6.h> 2462306a36Sopenharmony_ci#include <linux/inetdevice.h> 2562306a36Sopenharmony_ci#include <linux/igmp.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/if_ether.h> 2862306a36Sopenharmony_ci#include <linux/if_vlan.h> 2962306a36Sopenharmony_ci#include <linux/skbuff.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <net/ip.h> 3262306a36Sopenharmony_ci#include <net/arp.h> 3362306a36Sopenharmony_ci#include <net/route.h> 3462306a36Sopenharmony_ci#include <net/ipv6.h> 3562306a36Sopenharmony_ci#include <net/ip6_route.h> 3662306a36Sopenharmony_ci#include <net/iucv/af_iucv.h> 3762306a36Sopenharmony_ci#include <linux/hashtable.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "qeth_l3.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int qeth_l3_register_addr_entry(struct qeth_card *, 4262306a36Sopenharmony_ci struct qeth_ipaddr *); 4362306a36Sopenharmony_cistatic int qeth_l3_deregister_addr_entry(struct qeth_card *, 4462306a36Sopenharmony_ci struct qeth_ipaddr *); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr, 4762306a36Sopenharmony_ci char *buf) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci if (proto == QETH_PROT_IPV4) 5062306a36Sopenharmony_ci return scnprintf(buf, INET_ADDRSTRLEN, "%pI4", addr); 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci return scnprintf(buf, INET6_ADDRSTRLEN, "%pI6", addr); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card, 5662306a36Sopenharmony_ci struct qeth_ipaddr *query) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u32 key = qeth_l3_ipaddr_hash(query); 5962306a36Sopenharmony_ci struct qeth_ipaddr *addr; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (query->is_multicast) { 6262306a36Sopenharmony_ci hash_for_each_possible(card->rx_mode_addrs, addr, hnode, key) 6362306a36Sopenharmony_ci if (qeth_l3_addr_match_ip(addr, query)) 6462306a36Sopenharmony_ci return addr; 6562306a36Sopenharmony_ci } else { 6662306a36Sopenharmony_ci hash_for_each_possible(card->ip_htable, addr, hnode, key) 6762306a36Sopenharmony_ci if (qeth_l3_addr_match_ip(addr, query)) 6862306a36Sopenharmony_ci return addr; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci return NULL; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i, j; 7662306a36Sopenharmony_ci u8 octet; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (i = 0; i < len; ++i) { 7962306a36Sopenharmony_ci octet = addr[i]; 8062306a36Sopenharmony_ci for (j = 7; j >= 0; --j) { 8162306a36Sopenharmony_ci bits[i*8 + j] = octet & 1; 8262306a36Sopenharmony_ci octet >>= 1; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card, 8862306a36Sopenharmony_ci struct qeth_ipaddr *addr) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct qeth_ipato_entry *ipatoe; 9162306a36Sopenharmony_ci u8 addr_bits[128] = {0, }; 9262306a36Sopenharmony_ci u8 ipatoe_bits[128] = {0, }; 9362306a36Sopenharmony_ci int rc = 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!card->ipato.enabled) 9662306a36Sopenharmony_ci return false; 9762306a36Sopenharmony_ci if (addr->type != QETH_IP_TYPE_NORMAL) 9862306a36Sopenharmony_ci return false; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits, 10162306a36Sopenharmony_ci (addr->proto == QETH_PROT_IPV4) ? 4 : 16); 10262306a36Sopenharmony_ci list_for_each_entry(ipatoe, &card->ipato.entries, entry) { 10362306a36Sopenharmony_ci if (addr->proto != ipatoe->proto) 10462306a36Sopenharmony_ci continue; 10562306a36Sopenharmony_ci qeth_l3_convert_addr_to_bits(ipatoe->addr, ipatoe_bits, 10662306a36Sopenharmony_ci (ipatoe->proto == QETH_PROT_IPV4) ? 10762306a36Sopenharmony_ci 4 : 16); 10862306a36Sopenharmony_ci rc = !memcmp(addr_bits, ipatoe_bits, ipatoe->mask_bits); 10962306a36Sopenharmony_ci if (rc) 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci /* invert? */ 11362306a36Sopenharmony_ci if ((addr->proto == QETH_PROT_IPV4) && card->ipato.invert4) 11462306a36Sopenharmony_ci rc = !rc; 11562306a36Sopenharmony_ci else if ((addr->proto == QETH_PROT_IPV6) && card->ipato.invert6) 11662306a36Sopenharmony_ci rc = !rc; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return rc; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int qeth_l3_delete_ip(struct qeth_card *card, 12262306a36Sopenharmony_ci struct qeth_ipaddr *tmp_addr) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci int rc = 0; 12562306a36Sopenharmony_ci struct qeth_ipaddr *addr; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (tmp_addr->type == QETH_IP_TYPE_RXIP) 12862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "delrxip"); 12962306a36Sopenharmony_ci else if (tmp_addr->type == QETH_IP_TYPE_VIPA) 13062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "delvipa"); 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "delip"); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (tmp_addr->proto == QETH_PROT_IPV4) 13562306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4); 13662306a36Sopenharmony_ci else { 13762306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8); 13862306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci addr = qeth_l3_find_addr_by_ip(card, tmp_addr); 14262306a36Sopenharmony_ci if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr)) 14362306a36Sopenharmony_ci return -ENOENT; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci addr->ref_counter--; 14662306a36Sopenharmony_ci if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0) 14762306a36Sopenharmony_ci return rc; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (qeth_card_hw_is_reachable(card)) 15062306a36Sopenharmony_ci rc = qeth_l3_deregister_addr_entry(card, addr); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci hash_del(&addr->hnode); 15362306a36Sopenharmony_ci kfree(addr); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return rc; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci int rc = 0; 16162306a36Sopenharmony_ci struct qeth_ipaddr *addr; 16262306a36Sopenharmony_ci char buf[INET6_ADDRSTRLEN]; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (tmp_addr->type == QETH_IP_TYPE_RXIP) 16562306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "addrxip"); 16662306a36Sopenharmony_ci else if (tmp_addr->type == QETH_IP_TYPE_VIPA) 16762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "addvipa"); 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "addip"); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (tmp_addr->proto == QETH_PROT_IPV4) 17262306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4); 17362306a36Sopenharmony_ci else { 17462306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8); 17562306a36Sopenharmony_ci QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8); 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci addr = qeth_l3_find_addr_by_ip(card, tmp_addr); 17962306a36Sopenharmony_ci if (addr) { 18062306a36Sopenharmony_ci if (tmp_addr->type != QETH_IP_TYPE_NORMAL) 18162306a36Sopenharmony_ci return -EADDRINUSE; 18262306a36Sopenharmony_ci if (qeth_l3_addr_match_all(addr, tmp_addr)) { 18362306a36Sopenharmony_ci addr->ref_counter++; 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u, 18762306a36Sopenharmony_ci buf); 18862306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 18962306a36Sopenharmony_ci "Registering IP address %s failed\n", buf); 19062306a36Sopenharmony_ci return -EADDRINUSE; 19162306a36Sopenharmony_ci } else { 19262306a36Sopenharmony_ci addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL); 19362306a36Sopenharmony_ci if (!addr) 19462306a36Sopenharmony_ci return -ENOMEM; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (qeth_l3_is_addr_covered_by_ipato(card, addr)) { 19762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "tkovaddr"); 19862306a36Sopenharmony_ci addr->ipato = 1; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci hash_add(card->ip_htable, &addr->hnode, 20162306a36Sopenharmony_ci qeth_l3_ipaddr_hash(addr)); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (!qeth_card_hw_is_reachable(card)) { 20462306a36Sopenharmony_ci addr->disp_flag = QETH_DISP_ADDR_ADD; 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci rc = qeth_l3_register_addr_entry(card, addr); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { 21162306a36Sopenharmony_ci addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci hash_del(&addr->hnode); 21462306a36Sopenharmony_ci kfree(addr); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return rc; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int qeth_l3_modify_ip(struct qeth_card *card, struct qeth_ipaddr *addr, 22162306a36Sopenharmony_ci bool add) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int rc; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 22662306a36Sopenharmony_ci rc = add ? qeth_l3_add_ip(card, addr) : qeth_l3_delete_ip(card, addr); 22762306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return rc; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void qeth_l3_drain_rx_mode_cache(struct qeth_card *card) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct qeth_ipaddr *addr; 23562306a36Sopenharmony_ci struct hlist_node *tmp; 23662306a36Sopenharmony_ci int i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) { 23962306a36Sopenharmony_ci hash_del(&addr->hnode); 24062306a36Sopenharmony_ci kfree(addr); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct qeth_ipaddr *addr; 24762306a36Sopenharmony_ci struct hlist_node *tmp; 24862306a36Sopenharmony_ci int i; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "clearip"); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { 25562306a36Sopenharmony_ci if (!recover) { 25662306a36Sopenharmony_ci hash_del(&addr->hnode); 25762306a36Sopenharmony_ci kfree(addr); 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci /* prepare for recovery */ 26062306a36Sopenharmony_ci addr->disp_flag = QETH_DISP_ADDR_ADD; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void qeth_l3_recover_ip(struct qeth_card *card) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct qeth_ipaddr *addr; 27062306a36Sopenharmony_ci struct hlist_node *tmp; 27162306a36Sopenharmony_ci int i; 27262306a36Sopenharmony_ci int rc; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "recovrip"); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) { 27962306a36Sopenharmony_ci if (addr->disp_flag == QETH_DISP_ADDR_ADD) { 28062306a36Sopenharmony_ci rc = qeth_l3_register_addr_entry(card, addr); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) { 28362306a36Sopenharmony_ci /* keep it in the records */ 28462306a36Sopenharmony_ci addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci /* bad address */ 28762306a36Sopenharmony_ci hash_del(&addr->hnode); 28862306a36Sopenharmony_ci kfree(addr); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply, 29762306a36Sopenharmony_ci unsigned long data) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci switch (cmd->hdr.return_code) { 30262306a36Sopenharmony_ci case IPA_RC_SUCCESS: 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci case IPA_RC_DUPLICATE_IP_ADDRESS: 30562306a36Sopenharmony_ci return -EADDRINUSE; 30662306a36Sopenharmony_ci case IPA_RC_MC_ADDR_NOT_FOUND: 30762306a36Sopenharmony_ci return -ENOENT; 30862306a36Sopenharmony_ci case IPA_RC_LAN_OFFLINE: 30962306a36Sopenharmony_ci return -ENETDOWN; 31062306a36Sopenharmony_ci default: 31162306a36Sopenharmony_ci return -EIO; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int qeth_l3_send_setdelmc(struct qeth_card *card, 31662306a36Sopenharmony_ci struct qeth_ipaddr *addr, 31762306a36Sopenharmony_ci enum qeth_ipa_cmds ipacmd) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 32062306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "setdelmc"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto, 32562306a36Sopenharmony_ci IPA_DATA_SIZEOF(setdelipm)); 32662306a36Sopenharmony_ci if (!iob) 32762306a36Sopenharmony_ci return -ENOMEM; 32862306a36Sopenharmony_ci cmd = __ipa_cmd(iob); 32962306a36Sopenharmony_ci if (addr->proto == QETH_PROT_IPV6) { 33062306a36Sopenharmony_ci cmd->data.setdelipm.ip = addr->u.a6.addr; 33162306a36Sopenharmony_ci ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac); 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr; 33462306a36Sopenharmony_ci ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac); 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci unsigned int i = 0; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci while (len && i < 4) { 34562306a36Sopenharmony_ci int mask_len = min_t(int, len, 32); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci prefix->s6_addr32[i] = inet_make_mask(mask_len); 34862306a36Sopenharmony_ci len -= mask_len; 34962306a36Sopenharmony_ci i++; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic u32 qeth_l3_get_setdelip_flags(struct qeth_ipaddr *addr, bool set) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci switch (addr->type) { 35662306a36Sopenharmony_ci case QETH_IP_TYPE_RXIP: 35762306a36Sopenharmony_ci return (set) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0; 35862306a36Sopenharmony_ci case QETH_IP_TYPE_VIPA: 35962306a36Sopenharmony_ci return (set) ? QETH_IPA_SETIP_VIPA_FLAG : 36062306a36Sopenharmony_ci QETH_IPA_DELIP_VIPA_FLAG; 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci return (set && addr->ipato) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int qeth_l3_send_setdelip(struct qeth_card *card, 36762306a36Sopenharmony_ci struct qeth_ipaddr *addr, 36862306a36Sopenharmony_ci enum qeth_ipa_cmds ipacmd) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 37162306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 37262306a36Sopenharmony_ci u32 flags; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "setdelip"); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto, 37762306a36Sopenharmony_ci IPA_DATA_SIZEOF(setdelip6)); 37862306a36Sopenharmony_ci if (!iob) 37962306a36Sopenharmony_ci return -ENOMEM; 38062306a36Sopenharmony_ci cmd = __ipa_cmd(iob); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci flags = qeth_l3_get_setdelip_flags(addr, ipacmd == IPA_CMD_SETIP); 38362306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "flags%02X", flags); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (addr->proto == QETH_PROT_IPV6) { 38662306a36Sopenharmony_ci cmd->data.setdelip6.addr = addr->u.a6.addr; 38762306a36Sopenharmony_ci qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix, 38862306a36Sopenharmony_ci addr->u.a6.pfxlen); 38962306a36Sopenharmony_ci cmd->data.setdelip6.flags = flags; 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci cmd->data.setdelip4.addr = addr->u.a4.addr; 39262306a36Sopenharmony_ci cmd->data.setdelip4.mask = addr->u.a4.mask; 39362306a36Sopenharmony_ci cmd->data.setdelip4.flags = flags; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int qeth_l3_send_setrouting(struct qeth_card *card, 40062306a36Sopenharmony_ci enum qeth_routing_types type, enum qeth_prot_versions prot) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int rc; 40362306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 40462306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "setroutg"); 40762306a36Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, IPA_CMD_SETRTG, prot, 40862306a36Sopenharmony_ci IPA_DATA_SIZEOF(setrtg)); 40962306a36Sopenharmony_ci if (!iob) 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci cmd = __ipa_cmd(iob); 41262306a36Sopenharmony_ci cmd->data.setrtg.type = (type); 41362306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return rc; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int qeth_l3_correct_routing_type(struct qeth_card *card, 41962306a36Sopenharmony_ci enum qeth_routing_types *type, enum qeth_prot_versions prot) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci if (IS_IQD(card)) { 42262306a36Sopenharmony_ci switch (*type) { 42362306a36Sopenharmony_ci case NO_ROUTER: 42462306a36Sopenharmony_ci case PRIMARY_CONNECTOR: 42562306a36Sopenharmony_ci case SECONDARY_CONNECTOR: 42662306a36Sopenharmony_ci case MULTICAST_ROUTER: 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci goto out_inval; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci } else { 43262306a36Sopenharmony_ci switch (*type) { 43362306a36Sopenharmony_ci case NO_ROUTER: 43462306a36Sopenharmony_ci case PRIMARY_ROUTER: 43562306a36Sopenharmony_ci case SECONDARY_ROUTER: 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci case MULTICAST_ROUTER: 43862306a36Sopenharmony_ci if (qeth_is_ipafunc_supported(card, prot, 43962306a36Sopenharmony_ci IPA_OSA_MC_ROUTER)) 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci goto out_inval; 44262306a36Sopenharmony_ci default: 44362306a36Sopenharmony_ci goto out_inval; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ciout_inval: 44762306a36Sopenharmony_ci *type = NO_ROUTER; 44862306a36Sopenharmony_ci return -EINVAL; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ciint qeth_l3_setrouting_v4(struct qeth_card *card) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci int rc; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setrtg4"); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci rc = qeth_l3_correct_routing_type(card, &card->options.route4.type, 45862306a36Sopenharmony_ci QETH_PROT_IPV4); 45962306a36Sopenharmony_ci if (rc) 46062306a36Sopenharmony_ci return rc; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci rc = qeth_l3_send_setrouting(card, card->options.route4.type, 46362306a36Sopenharmony_ci QETH_PROT_IPV4); 46462306a36Sopenharmony_ci if (rc) { 46562306a36Sopenharmony_ci card->options.route4.type = NO_ROUTER; 46662306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n", 46762306a36Sopenharmony_ci rc, CARD_DEVID(card)); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci return rc; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ciint qeth_l3_setrouting_v6(struct qeth_card *card) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci int rc = 0; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setrtg6"); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_IPV6)) 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci rc = qeth_l3_correct_routing_type(card, &card->options.route6.type, 48162306a36Sopenharmony_ci QETH_PROT_IPV6); 48262306a36Sopenharmony_ci if (rc) 48362306a36Sopenharmony_ci return rc; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci rc = qeth_l3_send_setrouting(card, card->options.route6.type, 48662306a36Sopenharmony_ci QETH_PROT_IPV6); 48762306a36Sopenharmony_ci if (rc) { 48862306a36Sopenharmony_ci card->options.route6.type = NO_ROUTER; 48962306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n", 49062306a36Sopenharmony_ci rc, CARD_DEVID(card)); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci return rc; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/* 49662306a36Sopenharmony_ci * IP address takeover related functions 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs. 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * Caller must hold ip_lock. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_civoid qeth_l3_update_ipato(struct qeth_card *card) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct qeth_ipaddr *addr; 50762306a36Sopenharmony_ci unsigned int i; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci hash_for_each(card->ip_htable, i, addr, hnode) { 51062306a36Sopenharmony_ci if (addr->type != QETH_IP_TYPE_NORMAL) 51162306a36Sopenharmony_ci continue; 51262306a36Sopenharmony_ci addr->ipato = qeth_l3_is_addr_covered_by_ipato(card, addr); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void qeth_l3_clear_ipato_list(struct qeth_card *card) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct qeth_ipato_entry *ipatoe, *tmp; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { 52362306a36Sopenharmony_ci list_del(&ipatoe->entry); 52462306a36Sopenharmony_ci kfree(ipatoe); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci qeth_l3_update_ipato(card); 52862306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ciint qeth_l3_add_ipato_entry(struct qeth_card *card, 53262306a36Sopenharmony_ci struct qeth_ipato_entry *new) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct qeth_ipato_entry *ipatoe; 53562306a36Sopenharmony_ci int rc = 0; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "addipato"); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci list_for_each_entry(ipatoe, &card->ipato.entries, entry) { 54262306a36Sopenharmony_ci if (ipatoe->proto != new->proto) 54362306a36Sopenharmony_ci continue; 54462306a36Sopenharmony_ci if (!memcmp(ipatoe->addr, new->addr, 54562306a36Sopenharmony_ci (ipatoe->proto == QETH_PROT_IPV4) ? 4 : 16) && 54662306a36Sopenharmony_ci (ipatoe->mask_bits == new->mask_bits)) { 54762306a36Sopenharmony_ci rc = -EEXIST; 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!rc) { 55362306a36Sopenharmony_ci list_add_tail(&new->entry, &card->ipato.entries); 55462306a36Sopenharmony_ci qeth_l3_update_ipato(card); 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return rc; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciint qeth_l3_del_ipato_entry(struct qeth_card *card, 56362306a36Sopenharmony_ci enum qeth_prot_versions proto, u8 *addr, 56462306a36Sopenharmony_ci unsigned int mask_bits) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct qeth_ipato_entry *ipatoe, *tmp; 56762306a36Sopenharmony_ci int rc = -ENOENT; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "delipato"); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mutex_lock(&card->ip_lock); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) { 57462306a36Sopenharmony_ci if (ipatoe->proto != proto) 57562306a36Sopenharmony_ci continue; 57662306a36Sopenharmony_ci if (!memcmp(ipatoe->addr, addr, 57762306a36Sopenharmony_ci (proto == QETH_PROT_IPV4) ? 4 : 16) && 57862306a36Sopenharmony_ci (ipatoe->mask_bits == mask_bits)) { 57962306a36Sopenharmony_ci list_del(&ipatoe->entry); 58062306a36Sopenharmony_ci qeth_l3_update_ipato(card); 58162306a36Sopenharmony_ci kfree(ipatoe); 58262306a36Sopenharmony_ci rc = 0; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mutex_unlock(&card->ip_lock); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return rc; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ciint qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip, 59262306a36Sopenharmony_ci enum qeth_ip_types type, 59362306a36Sopenharmony_ci enum qeth_prot_versions proto) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct qeth_ipaddr addr; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci qeth_l3_init_ipaddr(&addr, type, proto); 59862306a36Sopenharmony_ci if (proto == QETH_PROT_IPV4) 59962306a36Sopenharmony_ci memcpy(&addr.u.a4.addr, ip, 4); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci memcpy(&addr.u.a6.addr, ip, 16); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci return qeth_l3_modify_ip(card, &addr, add); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ciint qeth_l3_modify_hsuid(struct qeth_card *card, bool add) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct qeth_ipaddr addr; 60962306a36Sopenharmony_ci unsigned int i; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); 61262306a36Sopenharmony_ci addr.u.a6.addr.s6_addr[0] = 0xfe; 61362306a36Sopenharmony_ci addr.u.a6.addr.s6_addr[1] = 0x80; 61462306a36Sopenharmony_ci for (i = 0; i < 8; i++) 61562306a36Sopenharmony_ci addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return qeth_l3_modify_ip(card, &addr, add); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int qeth_l3_register_addr_entry(struct qeth_card *card, 62162306a36Sopenharmony_ci struct qeth_ipaddr *addr) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci char buf[50]; 62462306a36Sopenharmony_ci int rc = 0; 62562306a36Sopenharmony_ci int cnt = 3; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (card->options.sniffer) 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (addr->proto == QETH_PROT_IPV4) { 63162306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "setaddr4"); 63262306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); 63362306a36Sopenharmony_ci } else if (addr->proto == QETH_PROT_IPV6) { 63462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "setaddr6"); 63562306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8); 63662306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8); 63762306a36Sopenharmony_ci } else { 63862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "setaddr?"); 63962306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr)); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci do { 64262306a36Sopenharmony_ci if (addr->is_multicast) 64362306a36Sopenharmony_ci rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM); 64462306a36Sopenharmony_ci else 64562306a36Sopenharmony_ci rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP); 64662306a36Sopenharmony_ci if (rc) 64762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "failed"); 64862306a36Sopenharmony_ci } while ((--cnt > 0) && rc); 64962306a36Sopenharmony_ci if (rc) { 65062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "FAILED"); 65162306a36Sopenharmony_ci qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf); 65262306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 65362306a36Sopenharmony_ci "Registering IP address %s failed\n", buf); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci return rc; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int qeth_l3_deregister_addr_entry(struct qeth_card *card, 65962306a36Sopenharmony_ci struct qeth_ipaddr *addr) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci int rc = 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (card->options.sniffer) 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (addr->proto == QETH_PROT_IPV4) { 66762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "deladdr4"); 66862306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int)); 66962306a36Sopenharmony_ci } else if (addr->proto == QETH_PROT_IPV6) { 67062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "deladdr6"); 67162306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8); 67262306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8); 67362306a36Sopenharmony_ci } else { 67462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "deladdr?"); 67562306a36Sopenharmony_ci QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr)); 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci if (addr->is_multicast) 67862306a36Sopenharmony_ci rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM); 67962306a36Sopenharmony_ci else 68062306a36Sopenharmony_ci rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP); 68162306a36Sopenharmony_ci if (rc) 68262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "failed"); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return rc; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic int qeth_l3_setadapter_parms(struct qeth_card *card) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int rc = 0; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "setadprm"); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) { 69462306a36Sopenharmony_ci rc = qeth_setadpparms_change_macaddr(card); 69562306a36Sopenharmony_ci if (rc) 69662306a36Sopenharmony_ci dev_warn(&card->gdev->dev, "Reading the adapter MAC" 69762306a36Sopenharmony_ci " address failed\n"); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return rc; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int qeth_l3_start_ipa_arp_processing(struct qeth_card *card) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci int rc; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "ipaarp"); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { 71062306a36Sopenharmony_ci dev_info(&card->gdev->dev, 71162306a36Sopenharmony_ci "ARP processing not supported on %s!\n", 71262306a36Sopenharmony_ci netdev_name(card->dev)); 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING, 71662306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 71762306a36Sopenharmony_ci if (rc) { 71862306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 71962306a36Sopenharmony_ci "Starting ARP processing support for %s failed\n", 72062306a36Sopenharmony_ci netdev_name(card->dev)); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci return rc; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic int qeth_l3_start_ipa_source_mac(struct qeth_card *card) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci int rc; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "stsrcmac"); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_SOURCE_MAC)) { 73262306a36Sopenharmony_ci dev_info(&card->gdev->dev, 73362306a36Sopenharmony_ci "Inbound source MAC-address not supported on %s\n", 73462306a36Sopenharmony_ci netdev_name(card->dev)); 73562306a36Sopenharmony_ci return -EOPNOTSUPP; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC, 73962306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 74062306a36Sopenharmony_ci if (rc) 74162306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 74262306a36Sopenharmony_ci "Starting source MAC-address support for %s failed\n", 74362306a36Sopenharmony_ci netdev_name(card->dev)); 74462306a36Sopenharmony_ci return rc; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic int qeth_l3_start_ipa_vlan(struct qeth_card *card) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci int rc = 0; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "strtvlan"); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_FULL_VLAN)) { 75462306a36Sopenharmony_ci dev_info(&card->gdev->dev, 75562306a36Sopenharmony_ci "VLAN not supported on %s\n", netdev_name(card->dev)); 75662306a36Sopenharmony_ci return -EOPNOTSUPP; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO, 76062306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 76162306a36Sopenharmony_ci if (rc) { 76262306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 76362306a36Sopenharmony_ci "Starting VLAN support for %s failed\n", 76462306a36Sopenharmony_ci netdev_name(card->dev)); 76562306a36Sopenharmony_ci } else { 76662306a36Sopenharmony_ci dev_info(&card->gdev->dev, "VLAN enabled\n"); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci return rc; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic int qeth_l3_start_ipa_multicast(struct qeth_card *card) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci int rc; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "stmcast"); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_MULTICASTING)) { 77862306a36Sopenharmony_ci dev_info(&card->gdev->dev, 77962306a36Sopenharmony_ci "Multicast not supported on %s\n", 78062306a36Sopenharmony_ci netdev_name(card->dev)); 78162306a36Sopenharmony_ci return -EOPNOTSUPP; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING, 78562306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 78662306a36Sopenharmony_ci if (rc) { 78762306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 78862306a36Sopenharmony_ci "Starting multicast support for %s failed\n", 78962306a36Sopenharmony_ci netdev_name(card->dev)); 79062306a36Sopenharmony_ci } else { 79162306a36Sopenharmony_ci dev_info(&card->gdev->dev, "Multicast enabled\n"); 79262306a36Sopenharmony_ci card->dev->flags |= IFF_MULTICAST; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci return rc; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic int qeth_l3_softsetup_ipv6(struct qeth_card *card) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci u32 ipv6_data = 3; 80062306a36Sopenharmony_ci int rc; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "softipv6"); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (IS_IQD(card)) 80562306a36Sopenharmony_ci goto out; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START, 80862306a36Sopenharmony_ci &ipv6_data); 80962306a36Sopenharmony_ci if (rc) { 81062306a36Sopenharmony_ci dev_err(&card->gdev->dev, 81162306a36Sopenharmony_ci "Activating IPv6 support for %s failed\n", 81262306a36Sopenharmony_ci netdev_name(card->dev)); 81362306a36Sopenharmony_ci return rc; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci rc = qeth_send_simple_setassparms_v6(card, IPA_IPV6, IPA_CMD_ASS_START, 81662306a36Sopenharmony_ci NULL); 81762306a36Sopenharmony_ci if (rc) { 81862306a36Sopenharmony_ci dev_err(&card->gdev->dev, 81962306a36Sopenharmony_ci "Activating IPv6 support for %s failed\n", 82062306a36Sopenharmony_ci netdev_name(card->dev)); 82162306a36Sopenharmony_ci return rc; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci rc = qeth_send_simple_setassparms_v6(card, IPA_PASSTHRU, 82462306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 82562306a36Sopenharmony_ci if (rc) { 82662306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 82762306a36Sopenharmony_ci "Enabling the passthrough mode for %s failed\n", 82862306a36Sopenharmony_ci netdev_name(card->dev)); 82962306a36Sopenharmony_ci return rc; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ciout: 83262306a36Sopenharmony_ci dev_info(&card->gdev->dev, "IPV6 enabled\n"); 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic int qeth_l3_start_ipa_ipv6(struct qeth_card *card) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "strtipv6"); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_IPV6)) { 84162306a36Sopenharmony_ci dev_info(&card->gdev->dev, 84262306a36Sopenharmony_ci "IPv6 not supported on %s\n", netdev_name(card->dev)); 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci return qeth_l3_softsetup_ipv6(card); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic int qeth_l3_start_ipa_broadcast(struct qeth_card *card) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u32 filter_data = 1; 85162306a36Sopenharmony_ci int rc; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "stbrdcst"); 85462306a36Sopenharmony_ci card->info.broadcast_capable = 0; 85562306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_FILTERING)) { 85662306a36Sopenharmony_ci dev_info(&card->gdev->dev, 85762306a36Sopenharmony_ci "Broadcast not supported on %s\n", 85862306a36Sopenharmony_ci netdev_name(card->dev)); 85962306a36Sopenharmony_ci rc = -EOPNOTSUPP; 86062306a36Sopenharmony_ci goto out; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_FILTERING, 86362306a36Sopenharmony_ci IPA_CMD_ASS_START, NULL); 86462306a36Sopenharmony_ci if (rc) { 86562306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 86662306a36Sopenharmony_ci "Enabling broadcast filtering for %s failed\n", 86762306a36Sopenharmony_ci netdev_name(card->dev)); 86862306a36Sopenharmony_ci goto out; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_FILTERING, 87262306a36Sopenharmony_ci IPA_CMD_ASS_CONFIGURE, &filter_data); 87362306a36Sopenharmony_ci if (rc) { 87462306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 87562306a36Sopenharmony_ci "Setting up broadcast filtering for %s failed\n", 87662306a36Sopenharmony_ci netdev_name(card->dev)); 87762306a36Sopenharmony_ci goto out; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO; 88062306a36Sopenharmony_ci dev_info(&card->gdev->dev, "Broadcast enabled\n"); 88162306a36Sopenharmony_ci rc = qeth_send_simple_setassparms(card, IPA_FILTERING, 88262306a36Sopenharmony_ci IPA_CMD_ASS_ENABLE, &filter_data); 88362306a36Sopenharmony_ci if (rc) { 88462306a36Sopenharmony_ci dev_warn(&card->gdev->dev, 88562306a36Sopenharmony_ci "Setting up broadcast echo filtering for %s failed\n", 88662306a36Sopenharmony_ci netdev_name(card->dev)); 88762306a36Sopenharmony_ci goto out; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO; 89062306a36Sopenharmony_ciout: 89162306a36Sopenharmony_ci if (card->info.broadcast_capable) 89262306a36Sopenharmony_ci card->dev->flags |= IFF_BROADCAST; 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci card->dev->flags &= ~IFF_BROADCAST; 89562306a36Sopenharmony_ci return rc; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic void qeth_l3_start_ipassists(struct qeth_card *card) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "strtipas"); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci qeth_l3_start_ipa_arp_processing(card); /* go on*/ 90362306a36Sopenharmony_ci qeth_l3_start_ipa_source_mac(card); /* go on*/ 90462306a36Sopenharmony_ci qeth_l3_start_ipa_vlan(card); /* go on*/ 90562306a36Sopenharmony_ci qeth_l3_start_ipa_multicast(card); /* go on*/ 90662306a36Sopenharmony_ci qeth_l3_start_ipa_ipv6(card); /* go on*/ 90762306a36Sopenharmony_ci qeth_l3_start_ipa_broadcast(card); /* go on*/ 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card, 91162306a36Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (cmd->hdr.return_code) 91662306a36Sopenharmony_ci return -EIO; 91762306a36Sopenharmony_ci if (!is_valid_ether_addr(cmd->data.create_destroy_addr.mac_addr)) 91862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci eth_hw_addr_set(card->dev, cmd->data.create_destroy_addr.mac_addr); 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int qeth_l3_iqd_read_initial_mac(struct qeth_card *card) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci int rc = 0; 92762306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "hsrmac"); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6, 93262306a36Sopenharmony_ci IPA_DATA_SIZEOF(create_destroy_addr)); 93362306a36Sopenharmony_ci if (!iob) 93462306a36Sopenharmony_ci return -ENOMEM; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, qeth_l3_iqd_read_initial_mac_cb, 93762306a36Sopenharmony_ci NULL); 93862306a36Sopenharmony_ci return rc; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int qeth_l3_get_unique_id_cb(struct qeth_card *card, 94262306a36Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 94562306a36Sopenharmony_ci u16 *uid = reply->param; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci if (cmd->hdr.return_code == 0) { 94862306a36Sopenharmony_ci *uid = cmd->data.create_destroy_addr.uid; 94962306a36Sopenharmony_ci return 0; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci dev_warn(&card->gdev->dev, "The network adapter failed to generate a unique ID\n"); 95362306a36Sopenharmony_ci return -EIO; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic u16 qeth_l3_get_unique_id(struct qeth_card *card, u16 uid) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "guniqeid"); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_IPV6)) 96362306a36Sopenharmony_ci goto out; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6, 96662306a36Sopenharmony_ci IPA_DATA_SIZEOF(create_destroy_addr)); 96762306a36Sopenharmony_ci if (!iob) 96862306a36Sopenharmony_ci goto out; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci __ipa_cmd(iob)->data.create_destroy_addr.uid = uid; 97162306a36Sopenharmony_ci qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, &uid); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ciout: 97462306a36Sopenharmony_ci return uid; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int 97862306a36Sopenharmony_ciqeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply, 97962306a36Sopenharmony_ci unsigned long data) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 98262306a36Sopenharmony_ci __u16 rc; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "diastrcb"); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci cmd = (struct qeth_ipa_cmd *)data; 98762306a36Sopenharmony_ci rc = cmd->hdr.return_code; 98862306a36Sopenharmony_ci if (rc) 98962306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "dxter%x", rc); 99062306a36Sopenharmony_ci switch (cmd->data.diagass.action) { 99162306a36Sopenharmony_ci case QETH_DIAGS_CMD_TRACE_QUERY: 99262306a36Sopenharmony_ci break; 99362306a36Sopenharmony_ci case QETH_DIAGS_CMD_TRACE_DISABLE: 99462306a36Sopenharmony_ci switch (rc) { 99562306a36Sopenharmony_ci case 0: 99662306a36Sopenharmony_ci case IPA_RC_INVALID_SUBCMD: 99762306a36Sopenharmony_ci card->info.promisc_mode = SET_PROMISC_MODE_OFF; 99862306a36Sopenharmony_ci dev_info(&card->gdev->dev, "The HiperSockets network " 99962306a36Sopenharmony_ci "traffic analyzer is deactivated\n"); 100062306a36Sopenharmony_ci break; 100162306a36Sopenharmony_ci default: 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci case QETH_DIAGS_CMD_TRACE_ENABLE: 100662306a36Sopenharmony_ci switch (rc) { 100762306a36Sopenharmony_ci case 0: 100862306a36Sopenharmony_ci card->info.promisc_mode = SET_PROMISC_MODE_ON; 100962306a36Sopenharmony_ci dev_info(&card->gdev->dev, "The HiperSockets network " 101062306a36Sopenharmony_ci "traffic analyzer is activated\n"); 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci case IPA_RC_HARDWARE_AUTH_ERROR: 101362306a36Sopenharmony_ci dev_warn(&card->gdev->dev, "The device is not " 101462306a36Sopenharmony_ci "authorized to run as a HiperSockets network " 101562306a36Sopenharmony_ci "traffic analyzer\n"); 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci case IPA_RC_TRACE_ALREADY_ACTIVE: 101862306a36Sopenharmony_ci dev_warn(&card->gdev->dev, "A HiperSockets " 101962306a36Sopenharmony_ci "network traffic analyzer is already " 102062306a36Sopenharmony_ci "active in the HiperSockets LAN\n"); 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci default: 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci default: 102762306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Unknown sniffer action (%#06x) on device %x\n", 102862306a36Sopenharmony_ci cmd->data.diagass.action, CARD_DEVID(card)); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci return rc ? -EIO : 0; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic int 103562306a36Sopenharmony_ciqeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) 103662306a36Sopenharmony_ci{ 103762306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 103862306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "diagtrac"); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci iob = qeth_get_diag_cmd(card, QETH_DIAGS_CMD_TRACE, 0); 104362306a36Sopenharmony_ci if (!iob) 104462306a36Sopenharmony_ci return -ENOMEM; 104562306a36Sopenharmony_ci cmd = __ipa_cmd(iob); 104662306a36Sopenharmony_ci cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET; 104762306a36Sopenharmony_ci cmd->data.diagass.action = diags_cmd; 104862306a36Sopenharmony_ci return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct qeth_card *card = arg; 105462306a36Sopenharmony_ci struct inet6_dev *in6_dev; 105562306a36Sopenharmony_ci struct in_device *in4_dev; 105662306a36Sopenharmony_ci struct qeth_ipaddr *ipm; 105762306a36Sopenharmony_ci struct qeth_ipaddr tmp; 105862306a36Sopenharmony_ci struct ip_mc_list *im4; 105962306a36Sopenharmony_ci struct ifmcaddr6 *im6; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "addmc"); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (!dev || !(dev->flags & IFF_UP)) 106462306a36Sopenharmony_ci goto out; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci in4_dev = __in_dev_get_rtnl(dev); 106762306a36Sopenharmony_ci if (!in4_dev) 106862306a36Sopenharmony_ci goto walk_ipv6; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); 107162306a36Sopenharmony_ci tmp.disp_flag = QETH_DISP_ADDR_ADD; 107262306a36Sopenharmony_ci tmp.is_multicast = 1; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL; 107562306a36Sopenharmony_ci im4 = rtnl_dereference(im4->next_rcu)) { 107662306a36Sopenharmony_ci tmp.u.a4.addr = im4->multiaddr; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci ipm = qeth_l3_find_addr_by_ip(card, &tmp); 107962306a36Sopenharmony_ci if (ipm) { 108062306a36Sopenharmony_ci /* for mcast, by-IP match means full match */ 108162306a36Sopenharmony_ci ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; 108262306a36Sopenharmony_ci continue; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL); 108662306a36Sopenharmony_ci if (!ipm) 108762306a36Sopenharmony_ci continue; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci hash_add(card->rx_mode_addrs, &ipm->hnode, 109062306a36Sopenharmony_ci qeth_l3_ipaddr_hash(ipm)); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ciwalk_ipv6: 109462306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_IPV6)) 109562306a36Sopenharmony_ci goto out; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci in6_dev = __in6_dev_get(dev); 109862306a36Sopenharmony_ci if (!in6_dev) 109962306a36Sopenharmony_ci goto out; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6); 110262306a36Sopenharmony_ci tmp.disp_flag = QETH_DISP_ADDR_ADD; 110362306a36Sopenharmony_ci tmp.is_multicast = 1; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci for (im6 = rtnl_dereference(in6_dev->mc_list); 110662306a36Sopenharmony_ci im6; 110762306a36Sopenharmony_ci im6 = rtnl_dereference(im6->next)) { 110862306a36Sopenharmony_ci tmp.u.a6.addr = im6->mca_addr; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci ipm = qeth_l3_find_addr_by_ip(card, &tmp); 111162306a36Sopenharmony_ci if (ipm) { 111262306a36Sopenharmony_ci /* for mcast, by-IP match means full match */ 111362306a36Sopenharmony_ci ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING; 111462306a36Sopenharmony_ci continue; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC); 111862306a36Sopenharmony_ci if (!ipm) 111962306a36Sopenharmony_ci continue; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci hash_add(card->rx_mode_addrs, &ipm->hnode, 112262306a36Sopenharmony_ci qeth_l3_ipaddr_hash(ipm)); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciout: 112762306a36Sopenharmony_ci return 0; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic void qeth_l3_set_promisc_mode(struct qeth_card *card) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci bool enable = card->dev->flags & IFF_PROMISC; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (card->info.promisc_mode == enable) 113562306a36Sopenharmony_ci return; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (IS_VM_NIC(card)) { /* Guestlan trace */ 113862306a36Sopenharmony_ci if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE)) 113962306a36Sopenharmony_ci qeth_setadp_promisc_mode(card, enable); 114062306a36Sopenharmony_ci } else if (card->options.sniffer && /* HiperSockets trace */ 114162306a36Sopenharmony_ci qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { 114262306a36Sopenharmony_ci if (enable) { 114362306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "+promisc"); 114462306a36Sopenharmony_ci qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE); 114562306a36Sopenharmony_ci } else { 114662306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "-promisc"); 114762306a36Sopenharmony_ci qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic void qeth_l3_rx_mode_work(struct work_struct *work) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct qeth_card *card = container_of(work, struct qeth_card, 115562306a36Sopenharmony_ci rx_mode_work); 115662306a36Sopenharmony_ci struct qeth_ipaddr *addr; 115762306a36Sopenharmony_ci struct hlist_node *tmp; 115862306a36Sopenharmony_ci int i, rc; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "setmulti"); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (!card->options.sniffer) { 116362306a36Sopenharmony_ci rtnl_lock(); 116462306a36Sopenharmony_ci qeth_l3_add_mcast_rtnl(card->dev, 0, card); 116562306a36Sopenharmony_ci if (qeth_is_supported(card, IPA_FULL_VLAN)) 116662306a36Sopenharmony_ci vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card); 116762306a36Sopenharmony_ci rtnl_unlock(); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) { 117062306a36Sopenharmony_ci switch (addr->disp_flag) { 117162306a36Sopenharmony_ci case QETH_DISP_ADDR_DELETE: 117262306a36Sopenharmony_ci rc = qeth_l3_deregister_addr_entry(card, addr); 117362306a36Sopenharmony_ci if (!rc || rc == -ENOENT) { 117462306a36Sopenharmony_ci hash_del(&addr->hnode); 117562306a36Sopenharmony_ci kfree(addr); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case QETH_DISP_ADDR_ADD: 117962306a36Sopenharmony_ci rc = qeth_l3_register_addr_entry(card, addr); 118062306a36Sopenharmony_ci if (rc && rc != -ENETDOWN) { 118162306a36Sopenharmony_ci hash_del(&addr->hnode); 118262306a36Sopenharmony_ci kfree(addr); 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci fallthrough; 118662306a36Sopenharmony_ci default: 118762306a36Sopenharmony_ci /* for next call to set_rx_mode(): */ 118862306a36Sopenharmony_ci addr->disp_flag = QETH_DISP_ADDR_DELETE; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci qeth_l3_set_promisc_mode(card); 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic int qeth_l3_arp_makerc(u16 rc) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci switch (rc) { 119962306a36Sopenharmony_ci case IPA_RC_SUCCESS: 120062306a36Sopenharmony_ci return 0; 120162306a36Sopenharmony_ci case QETH_IPA_ARP_RC_NOTSUPP: 120262306a36Sopenharmony_ci case QETH_IPA_ARP_RC_Q_NOTSUPP: 120362306a36Sopenharmony_ci return -EOPNOTSUPP; 120462306a36Sopenharmony_ci case QETH_IPA_ARP_RC_OUT_OF_RANGE: 120562306a36Sopenharmony_ci return -EINVAL; 120662306a36Sopenharmony_ci case QETH_IPA_ARP_RC_Q_NO_DATA: 120762306a36Sopenharmony_ci return -ENOENT; 120862306a36Sopenharmony_ci default: 120962306a36Sopenharmony_ci return -EIO; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic int qeth_l3_arp_cmd_cb(struct qeth_card *card, struct qeth_reply *reply, 121462306a36Sopenharmony_ci unsigned long data) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci qeth_setassparms_cb(card, reply, data); 121962306a36Sopenharmony_ci return qeth_l3_arp_makerc(cmd->hdr.return_code); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 122562306a36Sopenharmony_ci int rc; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpstnoe"); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* 123062306a36Sopenharmony_ci * currently GuestLAN only supports the ARP assist function 123162306a36Sopenharmony_ci * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES; 123262306a36Sopenharmony_ci * thus we say EOPNOTSUPP for this ARP function 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci if (IS_VM_NIC(card)) 123562306a36Sopenharmony_ci return -EOPNOTSUPP; 123662306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { 123762306a36Sopenharmony_ci return -EOPNOTSUPP; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, 124162306a36Sopenharmony_ci IPA_CMD_ASS_ARP_SET_NO_ENTRIES, 124262306a36Sopenharmony_ci SETASS_DATA_SIZEOF(flags_32bit), 124362306a36Sopenharmony_ci QETH_PROT_IPV4); 124462306a36Sopenharmony_ci if (!iob) 124562306a36Sopenharmony_ci return -ENOMEM; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (u32) no_entries; 124862306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); 124962306a36Sopenharmony_ci if (rc) 125062306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on device %x: %#x\n", 125162306a36Sopenharmony_ci CARD_DEVID(card), rc); 125262306a36Sopenharmony_ci return rc; 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic __u32 get_arp_entry_size(struct qeth_card *card, 125662306a36Sopenharmony_ci struct qeth_arp_query_data *qdata, 125762306a36Sopenharmony_ci struct qeth_arp_entrytype *type, __u8 strip_entries) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci __u32 rc; 126062306a36Sopenharmony_ci __u8 is_hsi; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci is_hsi = qdata->reply_bits == 5; 126362306a36Sopenharmony_ci if (type->ip == QETHARP_IP_ADDR_V4) { 126462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "arpev4"); 126562306a36Sopenharmony_ci if (strip_entries) { 126662306a36Sopenharmony_ci rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) : 126762306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry7_short); 126862306a36Sopenharmony_ci } else { 126962306a36Sopenharmony_ci rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) : 127062306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry7); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci } else if (type->ip == QETHARP_IP_ADDR_V6) { 127362306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "arpev6"); 127462306a36Sopenharmony_ci if (strip_entries) { 127562306a36Sopenharmony_ci rc = is_hsi ? 127662306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry5_short_ipv6) : 127762306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry7_short_ipv6); 127862306a36Sopenharmony_ci } else { 127962306a36Sopenharmony_ci rc = is_hsi ? 128062306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry5_ipv6) : 128162306a36Sopenharmony_ci sizeof(struct qeth_arp_qi_entry7_ipv6); 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci } else { 128462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "arpinv"); 128562306a36Sopenharmony_ci rc = 0; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci return rc; 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistatic int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) || 129462306a36Sopenharmony_ci (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int qeth_l3_arp_query_cb(struct qeth_card *card, 129862306a36Sopenharmony_ci struct qeth_reply *reply, unsigned long data) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 130162306a36Sopenharmony_ci struct qeth_arp_query_data *qdata; 130262306a36Sopenharmony_ci struct qeth_arp_query_info *qinfo; 130362306a36Sopenharmony_ci int e; 130462306a36Sopenharmony_ci int entrybytes_done; 130562306a36Sopenharmony_ci int stripped_bytes; 130662306a36Sopenharmony_ci __u8 do_strip_entries; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpquecb"); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci qinfo = (struct qeth_arp_query_info *) reply->param; 131162306a36Sopenharmony_ci cmd = (struct qeth_ipa_cmd *) data; 131262306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version); 131362306a36Sopenharmony_ci if (cmd->hdr.return_code) { 131462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "arpcberr"); 131562306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); 131662306a36Sopenharmony_ci return qeth_l3_arp_makerc(cmd->hdr.return_code); 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci if (cmd->data.setassparms.hdr.return_code) { 131962306a36Sopenharmony_ci cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code; 132062306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "setaperr"); 132162306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code); 132262306a36Sopenharmony_ci return qeth_l3_arp_makerc(cmd->hdr.return_code); 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci qdata = &cmd->data.setassparms.data.query_arp; 132562306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0; 132862306a36Sopenharmony_ci stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0; 132962306a36Sopenharmony_ci entrybytes_done = 0; 133062306a36Sopenharmony_ci for (e = 0; e < qdata->no_entries; ++e) { 133162306a36Sopenharmony_ci char *cur_entry; 133262306a36Sopenharmony_ci __u32 esize; 133362306a36Sopenharmony_ci struct qeth_arp_entrytype *etype; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci cur_entry = &qdata->data + entrybytes_done; 133662306a36Sopenharmony_ci etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type; 133762306a36Sopenharmony_ci if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) { 133862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "pmis"); 133962306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "%i", etype->ip); 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci esize = get_arp_entry_size(card, qdata, etype, 134362306a36Sopenharmony_ci do_strip_entries); 134462306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 5, "esz%i", esize); 134562306a36Sopenharmony_ci if (!esize) 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if ((qinfo->udata_len - qinfo->udata_offset) < esize) { 134962306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOSPC); 135062306a36Sopenharmony_ci memset(qinfo->udata, 0, 4); 135162306a36Sopenharmony_ci return -ENOSPC; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci memcpy(qinfo->udata + qinfo->udata_offset, 135562306a36Sopenharmony_ci &qdata->data + entrybytes_done + stripped_bytes, 135662306a36Sopenharmony_ci esize); 135762306a36Sopenharmony_ci entrybytes_done += esize + stripped_bytes; 135862306a36Sopenharmony_ci qinfo->udata_offset += esize; 135962306a36Sopenharmony_ci ++qinfo->no_entries; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci /* check if all replies received ... */ 136262306a36Sopenharmony_ci if (cmd->data.setassparms.hdr.seq_no < 136362306a36Sopenharmony_ci cmd->data.setassparms.hdr.number_of_replies) 136462306a36Sopenharmony_ci return 1; 136562306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries); 136662306a36Sopenharmony_ci memcpy(qinfo->udata, &qinfo->no_entries, 4); 136762306a36Sopenharmony_ci /* keep STRIP_ENTRIES flag so the user program can distinguish 136862306a36Sopenharmony_ci * stripped entries from normal ones */ 136962306a36Sopenharmony_ci if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) 137062306a36Sopenharmony_ci qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES; 137162306a36Sopenharmony_ci memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2); 137262306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 4, "rc%i", 0); 137362306a36Sopenharmony_ci return 0; 137462306a36Sopenharmony_ci} 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_cistatic int qeth_l3_query_arp_cache_info(struct qeth_card *card, 137762306a36Sopenharmony_ci enum qeth_prot_versions prot, 137862306a36Sopenharmony_ci struct qeth_arp_query_info *qinfo) 137962306a36Sopenharmony_ci{ 138062306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 138162306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd; 138262306a36Sopenharmony_ci int rc; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, 138762306a36Sopenharmony_ci IPA_CMD_ASS_ARP_QUERY_INFO, 138862306a36Sopenharmony_ci SETASS_DATA_SIZEOF(query_arp), prot); 138962306a36Sopenharmony_ci if (!iob) 139062306a36Sopenharmony_ci return -ENOMEM; 139162306a36Sopenharmony_ci cmd = __ipa_cmd(iob); 139262306a36Sopenharmony_ci cmd->data.setassparms.data.query_arp.request_bits = 0x000F; 139362306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_query_cb, qinfo); 139462306a36Sopenharmony_ci if (rc) 139562306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n", 139662306a36Sopenharmony_ci CARD_DEVID(card), rc); 139762306a36Sopenharmony_ci return rc; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci struct qeth_arp_query_info qinfo = {0, }; 140362306a36Sopenharmony_ci int rc; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpquery"); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/ 140862306a36Sopenharmony_ci IPA_ARP_PROCESSING)) { 140962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpqnsup"); 141062306a36Sopenharmony_ci rc = -EOPNOTSUPP; 141162306a36Sopenharmony_ci goto out; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci /* get size of userspace buffer and mask_bits -> 6 bytes */ 141462306a36Sopenharmony_ci if (copy_from_user(&qinfo, udata, 6)) { 141562306a36Sopenharmony_ci rc = -EFAULT; 141662306a36Sopenharmony_ci goto out; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); 141962306a36Sopenharmony_ci if (!qinfo.udata) { 142062306a36Sopenharmony_ci rc = -ENOMEM; 142162306a36Sopenharmony_ci goto out; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET; 142462306a36Sopenharmony_ci rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo); 142562306a36Sopenharmony_ci if (rc) { 142662306a36Sopenharmony_ci if (copy_to_user(udata, qinfo.udata, 4)) 142762306a36Sopenharmony_ci rc = -EFAULT; 142862306a36Sopenharmony_ci goto free_and_out; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) { 143162306a36Sopenharmony_ci /* fails in case of GuestLAN QDIO mode */ 143262306a36Sopenharmony_ci qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, &qinfo); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) { 143562306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "qactf"); 143662306a36Sopenharmony_ci rc = -EFAULT; 143762306a36Sopenharmony_ci goto free_and_out; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 4, "qacts"); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cifree_and_out: 144262306a36Sopenharmony_ci kfree(qinfo.udata); 144362306a36Sopenharmony_ciout: 144462306a36Sopenharmony_ci return rc; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic int qeth_l3_arp_modify_entry(struct qeth_card *card, 144862306a36Sopenharmony_ci struct qeth_arp_cache_entry *entry, 144962306a36Sopenharmony_ci enum qeth_arp_process_subcmds arp_cmd) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct qeth_arp_cache_entry *cmd_entry; 145262306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 145362306a36Sopenharmony_ci int rc; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (arp_cmd == IPA_CMD_ASS_ARP_ADD_ENTRY) 145662306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpadd"); 145762306a36Sopenharmony_ci else 145862306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpdel"); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* 146162306a36Sopenharmony_ci * currently GuestLAN only supports the ARP assist function 146262306a36Sopenharmony_ci * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY; 146362306a36Sopenharmony_ci * thus we say EOPNOTSUPP for this ARP function 146462306a36Sopenharmony_ci */ 146562306a36Sopenharmony_ci if (IS_VM_NIC(card)) 146662306a36Sopenharmony_ci return -EOPNOTSUPP; 146762306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { 146862306a36Sopenharmony_ci return -EOPNOTSUPP; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, arp_cmd, 147262306a36Sopenharmony_ci SETASS_DATA_SIZEOF(arp_entry), 147362306a36Sopenharmony_ci QETH_PROT_IPV4); 147462306a36Sopenharmony_ci if (!iob) 147562306a36Sopenharmony_ci return -ENOMEM; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci cmd_entry = &__ipa_cmd(iob)->data.setassparms.data.arp_entry; 147862306a36Sopenharmony_ci ether_addr_copy(cmd_entry->macaddr, entry->macaddr); 147962306a36Sopenharmony_ci memcpy(cmd_entry->ipaddr, entry->ipaddr, 4); 148062306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); 148162306a36Sopenharmony_ci if (rc) 148262306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Could not modify (cmd: %#x) ARP entry on device %x: %#x\n", 148362306a36Sopenharmony_ci arp_cmd, CARD_DEVID(card), rc); 148462306a36Sopenharmony_ci return rc; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic int qeth_l3_arp_flush_cache(struct qeth_card *card) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci struct qeth_cmd_buffer *iob; 149062306a36Sopenharmony_ci int rc; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "arpflush"); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* 149562306a36Sopenharmony_ci * currently GuestLAN only supports the ARP assist function 149662306a36Sopenharmony_ci * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE; 149762306a36Sopenharmony_ci * thus we say EOPNOTSUPP for this ARP function 149862306a36Sopenharmony_ci */ 149962306a36Sopenharmony_ci if (IS_VM_NIC(card) || IS_IQD(card)) 150062306a36Sopenharmony_ci return -EOPNOTSUPP; 150162306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) { 150262306a36Sopenharmony_ci return -EOPNOTSUPP; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, 150662306a36Sopenharmony_ci IPA_CMD_ASS_ARP_FLUSH_CACHE, 0, 150762306a36Sopenharmony_ci QETH_PROT_IPV4); 150862306a36Sopenharmony_ci if (!iob) 150962306a36Sopenharmony_ci return -ENOMEM; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL); 151262306a36Sopenharmony_ci if (rc) 151362306a36Sopenharmony_ci QETH_DBF_MESSAGE(2, "Could not flush ARP cache on device %x: %#x\n", 151462306a36Sopenharmony_ci CARD_DEVID(card), rc); 151562306a36Sopenharmony_ci return rc; 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cistatic int qeth_l3_ndo_siocdevprivate(struct net_device *dev, struct ifreq *rq, 151962306a36Sopenharmony_ci void __user *data, int cmd) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 152262306a36Sopenharmony_ci struct qeth_arp_cache_entry arp_entry; 152362306a36Sopenharmony_ci enum qeth_arp_process_subcmds arp_cmd; 152462306a36Sopenharmony_ci int rc = 0; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci switch (cmd) { 152762306a36Sopenharmony_ci case SIOC_QETH_ARP_SET_NO_ENTRIES: 152862306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 152962306a36Sopenharmony_ci rc = -EPERM; 153062306a36Sopenharmony_ci break; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci rc = qeth_l3_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue); 153362306a36Sopenharmony_ci break; 153462306a36Sopenharmony_ci case SIOC_QETH_ARP_QUERY_INFO: 153562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 153662306a36Sopenharmony_ci rc = -EPERM; 153762306a36Sopenharmony_ci break; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci rc = qeth_l3_arp_query(card, data); 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci case SIOC_QETH_ARP_ADD_ENTRY: 154262306a36Sopenharmony_ci case SIOC_QETH_ARP_REMOVE_ENTRY: 154362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 154462306a36Sopenharmony_ci return -EPERM; 154562306a36Sopenharmony_ci if (copy_from_user(&arp_entry, data, sizeof(arp_entry))) 154662306a36Sopenharmony_ci return -EFAULT; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci arp_cmd = (cmd == SIOC_QETH_ARP_ADD_ENTRY) ? 154962306a36Sopenharmony_ci IPA_CMD_ASS_ARP_ADD_ENTRY : 155062306a36Sopenharmony_ci IPA_CMD_ASS_ARP_REMOVE_ENTRY; 155162306a36Sopenharmony_ci return qeth_l3_arp_modify_entry(card, &arp_entry, arp_cmd); 155262306a36Sopenharmony_ci case SIOC_QETH_ARP_FLUSH_CACHE: 155362306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) { 155462306a36Sopenharmony_ci rc = -EPERM; 155562306a36Sopenharmony_ci break; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci rc = qeth_l3_arp_flush_cache(card); 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci default: 156062306a36Sopenharmony_ci rc = qeth_siocdevprivate(dev, rq, data, cmd); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci return rc; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst, 156662306a36Sopenharmony_ci __be16 proto) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci struct neighbour *n = NULL; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (dst) 157162306a36Sopenharmony_ci n = dst_neigh_lookup_skb(dst, skb); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci if (n) { 157462306a36Sopenharmony_ci int cast_type = n->type; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci neigh_release(n); 157762306a36Sopenharmony_ci if ((cast_type == RTN_BROADCAST) || 157862306a36Sopenharmony_ci (cast_type == RTN_MULTICAST) || 157962306a36Sopenharmony_ci (cast_type == RTN_ANYCAST)) 158062306a36Sopenharmony_ci return cast_type; 158162306a36Sopenharmony_ci return RTN_UNICAST; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci /* no neighbour (eg AF_PACKET), fall back to target's IP address ... */ 158562306a36Sopenharmony_ci switch (proto) { 158662306a36Sopenharmony_ci case htons(ETH_P_IP): 158762306a36Sopenharmony_ci if (ipv4_is_lbcast(ip_hdr(skb)->daddr)) 158862306a36Sopenharmony_ci return RTN_BROADCAST; 158962306a36Sopenharmony_ci return ipv4_is_multicast(ip_hdr(skb)->daddr) ? 159062306a36Sopenharmony_ci RTN_MULTICAST : RTN_UNICAST; 159162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 159262306a36Sopenharmony_ci return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ? 159362306a36Sopenharmony_ci RTN_MULTICAST : RTN_UNICAST; 159462306a36Sopenharmony_ci case htons(ETH_P_AF_IUCV): 159562306a36Sopenharmony_ci return RTN_UNICAST; 159662306a36Sopenharmony_ci default: 159762306a36Sopenharmony_ci /* OSA only: ... and MAC address */ 159862306a36Sopenharmony_ci return qeth_get_ether_cast_type(skb); 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cistatic int qeth_l3_get_cast_type(struct sk_buff *skb, __be16 proto) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci struct dst_entry *dst; 160562306a36Sopenharmony_ci int cast_type; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci rcu_read_lock(); 160862306a36Sopenharmony_ci dst = qeth_dst_check_rcu(skb, proto); 160962306a36Sopenharmony_ci cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto); 161062306a36Sopenharmony_ci rcu_read_unlock(); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci return cast_type; 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic u8 qeth_l3_cast_type_to_flag(int cast_type) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci if (cast_type == RTN_MULTICAST) 161862306a36Sopenharmony_ci return QETH_CAST_MULTICAST; 161962306a36Sopenharmony_ci if (cast_type == RTN_ANYCAST) 162062306a36Sopenharmony_ci return QETH_CAST_ANYCAST; 162162306a36Sopenharmony_ci if (cast_type == RTN_BROADCAST) 162262306a36Sopenharmony_ci return QETH_CAST_BROADCAST; 162362306a36Sopenharmony_ci return QETH_CAST_UNICAST; 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic void qeth_l3_fill_header(struct qeth_qdio_out_q *queue, 162762306a36Sopenharmony_ci struct qeth_hdr *hdr, struct sk_buff *skb, 162862306a36Sopenharmony_ci __be16 proto, unsigned int data_len) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3; 163162306a36Sopenharmony_ci struct vlan_ethhdr *veth = vlan_eth_hdr(skb); 163262306a36Sopenharmony_ci struct qeth_card *card = queue->card; 163362306a36Sopenharmony_ci struct dst_entry *dst; 163462306a36Sopenharmony_ci int cast_type; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci hdr->hdr.l3.length = data_len; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (skb_is_gso(skb)) { 163962306a36Sopenharmony_ci hdr->hdr.l3.id = QETH_HEADER_TYPE_L3_TSO; 164062306a36Sopenharmony_ci } else { 164162306a36Sopenharmony_ci hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 164462306a36Sopenharmony_ci qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, proto); 164562306a36Sopenharmony_ci /* some HW requires combined L3+L4 csum offload: */ 164662306a36Sopenharmony_ci if (proto == htons(ETH_P_IP)) 164762306a36Sopenharmony_ci hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (proto == htons(ETH_P_IP) || IS_IQD(card)) { 165262306a36Sopenharmony_ci /* NETIF_F_HW_VLAN_CTAG_TX */ 165362306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 165462306a36Sopenharmony_ci hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_VLAN_FRAME; 165562306a36Sopenharmony_ci hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb); 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci } else if (veth->h_vlan_proto == htons(ETH_P_8021Q)) { 165862306a36Sopenharmony_ci hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_INCLUDE_VLAN_TAG; 165962306a36Sopenharmony_ci hdr->hdr.l3.vlan_id = ntohs(veth->h_vlan_TCI); 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci rcu_read_lock(); 166362306a36Sopenharmony_ci dst = qeth_dst_check_rcu(skb, proto); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (IS_IQD(card) && skb_get_queue_mapping(skb) != QETH_IQD_MCAST_TXQ) 166662306a36Sopenharmony_ci cast_type = RTN_UNICAST; 166762306a36Sopenharmony_ci else 166862306a36Sopenharmony_ci cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto); 166962306a36Sopenharmony_ci l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci switch (proto) { 167262306a36Sopenharmony_ci case htons(ETH_P_IP): 167362306a36Sopenharmony_ci l3_hdr->next_hop.addr.s6_addr32[3] = 167462306a36Sopenharmony_ci qeth_next_hop_v4_rcu(skb, dst); 167562306a36Sopenharmony_ci break; 167662306a36Sopenharmony_ci case htons(ETH_P_IPV6): 167762306a36Sopenharmony_ci l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci hdr->hdr.l3.flags |= QETH_HDR_IPV6; 168062306a36Sopenharmony_ci if (!IS_IQD(card)) 168162306a36Sopenharmony_ci hdr->hdr.l3.flags |= QETH_HDR_PASSTHRU; 168262306a36Sopenharmony_ci break; 168362306a36Sopenharmony_ci case htons(ETH_P_AF_IUCV): 168462306a36Sopenharmony_ci l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80); 168562306a36Sopenharmony_ci memcpy(&l3_hdr->next_hop.addr.s6_addr32[2], 168662306a36Sopenharmony_ci iucv_trans_hdr(skb)->destUserID, 8); 168762306a36Sopenharmony_ci l3_hdr->flags |= QETH_HDR_IPV6; 168862306a36Sopenharmony_ci break; 168962306a36Sopenharmony_ci default: 169062306a36Sopenharmony_ci /* OSA only: */ 169162306a36Sopenharmony_ci l3_hdr->flags |= QETH_HDR_PASSTHRU; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci rcu_read_unlock(); 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_cistatic void qeth_l3_fixup_headers(struct sk_buff *skb) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci /* this is safe, IPv6 traffic takes a different path */ 170162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 170262306a36Sopenharmony_ci iph->check = 0; 170362306a36Sopenharmony_ci if (skb_is_gso(skb)) { 170462306a36Sopenharmony_ci iph->tot_len = 0; 170562306a36Sopenharmony_ci tcp_hdr(skb)->check = ~tcp_v4_check(0, iph->saddr, 170662306a36Sopenharmony_ci iph->daddr, 0); 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci} 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_cistatic int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb, 171162306a36Sopenharmony_ci struct qeth_qdio_out_q *queue, __be16 proto) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci unsigned int hw_hdr_len; 171462306a36Sopenharmony_ci int rc; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* re-use the L2 header area for the HW header: */ 171762306a36Sopenharmony_ci hw_hdr_len = skb_is_gso(skb) ? sizeof(struct qeth_hdr_tso) : 171862306a36Sopenharmony_ci sizeof(struct qeth_hdr); 171962306a36Sopenharmony_ci rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN); 172062306a36Sopenharmony_ci if (rc) 172162306a36Sopenharmony_ci return rc; 172262306a36Sopenharmony_ci skb_pull(skb, ETH_HLEN); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci qeth_l3_fixup_headers(skb); 172562306a36Sopenharmony_ci return qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header); 172662306a36Sopenharmony_ci} 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_cistatic netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, 172962306a36Sopenharmony_ci struct net_device *dev) 173062306a36Sopenharmony_ci{ 173162306a36Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 173262306a36Sopenharmony_ci __be16 proto = vlan_get_protocol(skb); 173362306a36Sopenharmony_ci u16 txq = skb_get_queue_mapping(skb); 173462306a36Sopenharmony_ci struct qeth_qdio_out_q *queue; 173562306a36Sopenharmony_ci int rc; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (!skb_is_gso(skb)) 173862306a36Sopenharmony_ci qdisc_skb_cb(skb)->pkt_len = skb->len; 173962306a36Sopenharmony_ci if (IS_IQD(card)) { 174062306a36Sopenharmony_ci queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)]; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (card->options.sniffer) 174362306a36Sopenharmony_ci goto tx_drop; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci switch (proto) { 174662306a36Sopenharmony_ci case htons(ETH_P_AF_IUCV): 174762306a36Sopenharmony_ci if (card->options.cq != QETH_CQ_ENABLED) 174862306a36Sopenharmony_ci goto tx_drop; 174962306a36Sopenharmony_ci break; 175062306a36Sopenharmony_ci case htons(ETH_P_IP): 175162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 175262306a36Sopenharmony_ci if (card->options.cq == QETH_CQ_ENABLED) 175362306a36Sopenharmony_ci goto tx_drop; 175462306a36Sopenharmony_ci break; 175562306a36Sopenharmony_ci default: 175662306a36Sopenharmony_ci goto tx_drop; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci } else { 175962306a36Sopenharmony_ci queue = card->qdio.out_qs[txq]; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (!(dev->flags & IFF_BROADCAST) && 176362306a36Sopenharmony_ci qeth_l3_get_cast_type(skb, proto) == RTN_BROADCAST) 176462306a36Sopenharmony_ci goto tx_drop; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if (proto == htons(ETH_P_IP) || IS_IQD(card)) 176762306a36Sopenharmony_ci rc = qeth_l3_xmit(card, skb, queue, proto); 176862306a36Sopenharmony_ci else 176962306a36Sopenharmony_ci rc = qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (!rc) 177262306a36Sopenharmony_ci return NETDEV_TX_OK; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_citx_drop: 177562306a36Sopenharmony_ci QETH_TXQ_STAT_INC(queue, tx_dropped); 177662306a36Sopenharmony_ci kfree_skb(skb); 177762306a36Sopenharmony_ci return NETDEV_TX_OK; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_cistatic void qeth_l3_set_rx_mode(struct net_device *dev) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci struct qeth_card *card = dev->ml_priv; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci schedule_work(&card->rx_mode_work); 178562306a36Sopenharmony_ci} 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci/* 178862306a36Sopenharmony_ci * we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting 178962306a36Sopenharmony_ci * NOARP on the netdevice is no option because it also turns off neighbor 179062306a36Sopenharmony_ci * solicitation. For IPv4 we install a neighbor_setup function. We don't want 179162306a36Sopenharmony_ci * arp resolution but we want the hard header (packet socket will work 179262306a36Sopenharmony_ci * e.g. tcpdump) 179362306a36Sopenharmony_ci */ 179462306a36Sopenharmony_cistatic int qeth_l3_neigh_setup_noarp(struct neighbour *n) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci n->nud_state = NUD_NOARP; 179762306a36Sopenharmony_ci memcpy(n->ha, "FAKELL", 6); 179862306a36Sopenharmony_ci n->output = n->ops->connected_output; 179962306a36Sopenharmony_ci return 0; 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic int 180362306a36Sopenharmony_ciqeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci if (np->tbl->family == AF_INET) 180662306a36Sopenharmony_ci np->neigh_setup = qeth_l3_neigh_setup_noarp; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci return 0; 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_cistatic netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb, 181262306a36Sopenharmony_ci struct net_device *dev, 181362306a36Sopenharmony_ci netdev_features_t features) 181462306a36Sopenharmony_ci{ 181562306a36Sopenharmony_ci if (vlan_get_protocol(skb) != htons(ETH_P_IP)) 181662306a36Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 181762306a36Sopenharmony_ci return qeth_features_check(skb, dev, features); 181862306a36Sopenharmony_ci} 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_cistatic u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb, 182162306a36Sopenharmony_ci struct net_device *sb_dev) 182262306a36Sopenharmony_ci{ 182362306a36Sopenharmony_ci __be16 proto = vlan_get_protocol(skb); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci return qeth_iqd_select_queue(dev, skb, 182662306a36Sopenharmony_ci qeth_l3_get_cast_type(skb, proto), sb_dev); 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic const struct net_device_ops qeth_l3_netdev_ops = { 183062306a36Sopenharmony_ci .ndo_open = qeth_open, 183162306a36Sopenharmony_ci .ndo_stop = qeth_stop, 183262306a36Sopenharmony_ci .ndo_get_stats64 = qeth_get_stats64, 183362306a36Sopenharmony_ci .ndo_start_xmit = qeth_l3_hard_start_xmit, 183462306a36Sopenharmony_ci .ndo_select_queue = qeth_l3_iqd_select_queue, 183562306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 183662306a36Sopenharmony_ci .ndo_set_rx_mode = qeth_l3_set_rx_mode, 183762306a36Sopenharmony_ci .ndo_eth_ioctl = qeth_do_ioctl, 183862306a36Sopenharmony_ci .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate, 183962306a36Sopenharmony_ci .ndo_fix_features = qeth_fix_features, 184062306a36Sopenharmony_ci .ndo_set_features = qeth_set_features, 184162306a36Sopenharmony_ci .ndo_tx_timeout = qeth_tx_timeout, 184262306a36Sopenharmony_ci}; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_cistatic const struct net_device_ops qeth_l3_osa_netdev_ops = { 184562306a36Sopenharmony_ci .ndo_open = qeth_open, 184662306a36Sopenharmony_ci .ndo_stop = qeth_stop, 184762306a36Sopenharmony_ci .ndo_get_stats64 = qeth_get_stats64, 184862306a36Sopenharmony_ci .ndo_start_xmit = qeth_l3_hard_start_xmit, 184962306a36Sopenharmony_ci .ndo_features_check = qeth_l3_osa_features_check, 185062306a36Sopenharmony_ci .ndo_select_queue = qeth_osa_select_queue, 185162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 185262306a36Sopenharmony_ci .ndo_set_rx_mode = qeth_l3_set_rx_mode, 185362306a36Sopenharmony_ci .ndo_eth_ioctl = qeth_do_ioctl, 185462306a36Sopenharmony_ci .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate, 185562306a36Sopenharmony_ci .ndo_fix_features = qeth_fix_features, 185662306a36Sopenharmony_ci .ndo_set_features = qeth_set_features, 185762306a36Sopenharmony_ci .ndo_tx_timeout = qeth_tx_timeout, 185862306a36Sopenharmony_ci .ndo_neigh_setup = qeth_l3_neigh_setup, 185962306a36Sopenharmony_ci}; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_cistatic int qeth_l3_setup_netdev(struct qeth_card *card) 186262306a36Sopenharmony_ci{ 186362306a36Sopenharmony_ci struct net_device *dev = card->dev; 186462306a36Sopenharmony_ci unsigned int headroom; 186562306a36Sopenharmony_ci int rc; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci if (IS_OSD(card) || IS_OSX(card)) { 186862306a36Sopenharmony_ci card->dev->netdev_ops = &qeth_l3_osa_netdev_ops; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci /*IPv6 address autoconfiguration stuff*/ 187162306a36Sopenharmony_ci dev->dev_id = qeth_l3_get_unique_id(card, dev->dev_id); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (!IS_VM_NIC(card)) { 187462306a36Sopenharmony_ci card->dev->features |= NETIF_F_SG; 187562306a36Sopenharmony_ci card->dev->hw_features |= NETIF_F_TSO | 187662306a36Sopenharmony_ci NETIF_F_RXCSUM | NETIF_F_IP_CSUM; 187762306a36Sopenharmony_ci card->dev->vlan_features |= NETIF_F_TSO | 187862306a36Sopenharmony_ci NETIF_F_RXCSUM | NETIF_F_IP_CSUM; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) { 188262306a36Sopenharmony_ci card->dev->hw_features |= NETIF_F_IPV6_CSUM; 188362306a36Sopenharmony_ci card->dev->vlan_features |= NETIF_F_IPV6_CSUM; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) { 188662306a36Sopenharmony_ci card->dev->hw_features |= NETIF_F_TSO6; 188762306a36Sopenharmony_ci card->dev->vlan_features |= NETIF_F_TSO6; 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci /* allow for de-acceleration of NETIF_F_HW_VLAN_CTAG_TX: */ 189162306a36Sopenharmony_ci if (card->dev->hw_features & NETIF_F_TSO6) 189262306a36Sopenharmony_ci headroom = sizeof(struct qeth_hdr_tso) + VLAN_HLEN; 189362306a36Sopenharmony_ci else if (card->dev->hw_features & NETIF_F_TSO) 189462306a36Sopenharmony_ci headroom = sizeof(struct qeth_hdr_tso); 189562306a36Sopenharmony_ci else 189662306a36Sopenharmony_ci headroom = sizeof(struct qeth_hdr) + VLAN_HLEN; 189762306a36Sopenharmony_ci } else if (IS_IQD(card)) { 189862306a36Sopenharmony_ci card->dev->flags |= IFF_NOARP; 189962306a36Sopenharmony_ci card->dev->netdev_ops = &qeth_l3_netdev_ops; 190062306a36Sopenharmony_ci headroom = sizeof(struct qeth_hdr) - ETH_HLEN; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci rc = qeth_l3_iqd_read_initial_mac(card); 190362306a36Sopenharmony_ci if (rc) 190462306a36Sopenharmony_ci return rc; 190562306a36Sopenharmony_ci } else 190662306a36Sopenharmony_ci return -ENODEV; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci card->dev->needed_headroom = headroom; 190962306a36Sopenharmony_ci card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX | 191062306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci netif_keep_dst(card->dev); 191362306a36Sopenharmony_ci if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) 191462306a36Sopenharmony_ci netif_set_tso_max_size(card->dev, 191562306a36Sopenharmony_ci PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1)); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci netif_napi_add(card->dev, &card->napi, qeth_poll); 191862306a36Sopenharmony_ci return register_netdev(card->dev); 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_cistatic const struct device_type qeth_l3_devtype = { 192262306a36Sopenharmony_ci .name = "qeth_layer3", 192362306a36Sopenharmony_ci .groups = qeth_l3_attr_groups, 192462306a36Sopenharmony_ci}; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_cistatic int qeth_l3_probe_device(struct ccwgroup_device *gdev) 192762306a36Sopenharmony_ci{ 192862306a36Sopenharmony_ci struct qeth_card *card = dev_get_drvdata(&gdev->dev); 192962306a36Sopenharmony_ci int rc; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci hash_init(card->ip_htable); 193262306a36Sopenharmony_ci mutex_init(&card->ip_lock); 193362306a36Sopenharmony_ci card->cmd_wq = alloc_ordered_workqueue("%s_cmd", 0, 193462306a36Sopenharmony_ci dev_name(&gdev->dev)); 193562306a36Sopenharmony_ci if (!card->cmd_wq) 193662306a36Sopenharmony_ci return -ENOMEM; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci if (gdev->dev.type) { 193962306a36Sopenharmony_ci rc = device_add_groups(&gdev->dev, qeth_l3_attr_groups); 194062306a36Sopenharmony_ci if (rc) { 194162306a36Sopenharmony_ci destroy_workqueue(card->cmd_wq); 194262306a36Sopenharmony_ci return rc; 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci } else { 194562306a36Sopenharmony_ci gdev->dev.type = &qeth_l3_devtype; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci INIT_WORK(&card->rx_mode_work, qeth_l3_rx_mode_work); 194962306a36Sopenharmony_ci return 0; 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic void qeth_l3_remove_device(struct ccwgroup_device *cgdev) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct qeth_card *card = dev_get_drvdata(&cgdev->dev); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci if (cgdev->dev.type != &qeth_l3_devtype) 195762306a36Sopenharmony_ci device_remove_groups(&cgdev->dev, qeth_l3_attr_groups); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 196062306a36Sopenharmony_ci wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (cgdev->state == CCWGROUP_ONLINE) 196362306a36Sopenharmony_ci qeth_set_offline(card, card->discipline, false); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (card->dev->reg_state == NETREG_REGISTERED) 196662306a36Sopenharmony_ci unregister_netdev(card->dev); 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci destroy_workqueue(card->cmd_wq); 196962306a36Sopenharmony_ci qeth_l3_clear_ip_htable(card, 0); 197062306a36Sopenharmony_ci qeth_l3_clear_ipato_list(card); 197162306a36Sopenharmony_ci} 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_cistatic int qeth_l3_set_online(struct qeth_card *card, bool carrier_ok) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci struct net_device *dev = card->dev; 197662306a36Sopenharmony_ci int rc = 0; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci /* softsetup */ 197962306a36Sopenharmony_ci QETH_CARD_TEXT(card, 2, "softsetp"); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci rc = qeth_l3_setadapter_parms(card); 198262306a36Sopenharmony_ci if (rc) 198362306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "2err%04x", rc); 198462306a36Sopenharmony_ci if (!card->options.sniffer) { 198562306a36Sopenharmony_ci qeth_l3_start_ipassists(card); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci rc = qeth_l3_setrouting_v4(card); 198862306a36Sopenharmony_ci if (rc) 198962306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "4err%04x", rc); 199062306a36Sopenharmony_ci rc = qeth_l3_setrouting_v6(card); 199162306a36Sopenharmony_ci if (rc) 199262306a36Sopenharmony_ci QETH_CARD_TEXT_(card, 2, "5err%04x", rc); 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci card->state = CARD_STATE_SOFTSETUP; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci qeth_set_allowed_threads(card, 0xffffffff, 0); 199862306a36Sopenharmony_ci qeth_l3_recover_ip(card); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci if (dev->reg_state != NETREG_REGISTERED) { 200162306a36Sopenharmony_ci rc = qeth_l3_setup_netdev(card); 200262306a36Sopenharmony_ci if (rc) 200362306a36Sopenharmony_ci goto err_setup; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci if (carrier_ok) 200662306a36Sopenharmony_ci netif_carrier_on(dev); 200762306a36Sopenharmony_ci } else { 200862306a36Sopenharmony_ci rtnl_lock(); 200962306a36Sopenharmony_ci rc = qeth_set_real_num_tx_queues(card, 201062306a36Sopenharmony_ci qeth_tx_actual_queues(card)); 201162306a36Sopenharmony_ci if (rc) { 201262306a36Sopenharmony_ci rtnl_unlock(); 201362306a36Sopenharmony_ci goto err_set_queues; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (carrier_ok) 201762306a36Sopenharmony_ci netif_carrier_on(dev); 201862306a36Sopenharmony_ci else 201962306a36Sopenharmony_ci netif_carrier_off(dev); 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci netif_device_attach(dev); 202262306a36Sopenharmony_ci qeth_enable_hw_features(dev); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (netif_running(dev)) { 202562306a36Sopenharmony_ci local_bh_disable(); 202662306a36Sopenharmony_ci napi_schedule(&card->napi); 202762306a36Sopenharmony_ci /* kick-start the NAPI softirq: */ 202862306a36Sopenharmony_ci local_bh_enable(); 202962306a36Sopenharmony_ci } 203062306a36Sopenharmony_ci rtnl_unlock(); 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci return 0; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_cierr_set_queues: 203562306a36Sopenharmony_cierr_setup: 203662306a36Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 203762306a36Sopenharmony_ci card->state = CARD_STATE_DOWN; 203862306a36Sopenharmony_ci qeth_l3_clear_ip_htable(card, 1); 203962306a36Sopenharmony_ci return rc; 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic void qeth_l3_set_offline(struct qeth_card *card) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci qeth_set_allowed_threads(card, 0, 1); 204562306a36Sopenharmony_ci qeth_l3_drain_rx_mode_cache(card); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (card->options.sniffer && 204862306a36Sopenharmony_ci (card->info.promisc_mode == SET_PROMISC_MODE_ON)) 204962306a36Sopenharmony_ci qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (card->state == CARD_STATE_SOFTSETUP) { 205262306a36Sopenharmony_ci card->state = CARD_STATE_DOWN; 205362306a36Sopenharmony_ci qeth_l3_clear_ip_htable(card, 1); 205462306a36Sopenharmony_ci } 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci/* Returns zero if the command is successfully "consumed" */ 205862306a36Sopenharmony_cistatic int qeth_l3_control_event(struct qeth_card *card, 205962306a36Sopenharmony_ci struct qeth_ipa_cmd *cmd) 206062306a36Sopenharmony_ci{ 206162306a36Sopenharmony_ci return 1; 206262306a36Sopenharmony_ci} 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ciconst struct qeth_discipline qeth_l3_discipline = { 206562306a36Sopenharmony_ci .setup = qeth_l3_probe_device, 206662306a36Sopenharmony_ci .remove = qeth_l3_remove_device, 206762306a36Sopenharmony_ci .set_online = qeth_l3_set_online, 206862306a36Sopenharmony_ci .set_offline = qeth_l3_set_offline, 206962306a36Sopenharmony_ci .control_event_handler = qeth_l3_control_event, 207062306a36Sopenharmony_ci}; 207162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qeth_l3_discipline); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic int qeth_l3_handle_ip_event(struct qeth_card *card, 207462306a36Sopenharmony_ci struct qeth_ipaddr *addr, 207562306a36Sopenharmony_ci unsigned long event) 207662306a36Sopenharmony_ci{ 207762306a36Sopenharmony_ci switch (event) { 207862306a36Sopenharmony_ci case NETDEV_UP: 207962306a36Sopenharmony_ci qeth_l3_modify_ip(card, addr, true); 208062306a36Sopenharmony_ci return NOTIFY_OK; 208162306a36Sopenharmony_ci case NETDEV_DOWN: 208262306a36Sopenharmony_ci qeth_l3_modify_ip(card, addr, false); 208362306a36Sopenharmony_ci return NOTIFY_OK; 208462306a36Sopenharmony_ci default: 208562306a36Sopenharmony_ci return NOTIFY_DONE; 208662306a36Sopenharmony_ci } 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistruct qeth_l3_ip_event_work { 209062306a36Sopenharmony_ci struct work_struct work; 209162306a36Sopenharmony_ci struct qeth_card *card; 209262306a36Sopenharmony_ci struct qeth_ipaddr addr; 209362306a36Sopenharmony_ci}; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci#define to_ip_work(w) container_of((w), struct qeth_l3_ip_event_work, work) 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_cistatic void qeth_l3_add_ip_worker(struct work_struct *work) 209862306a36Sopenharmony_ci{ 209962306a36Sopenharmony_ci struct qeth_l3_ip_event_work *ip_work = to_ip_work(work); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci qeth_l3_modify_ip(ip_work->card, &ip_work->addr, true); 210262306a36Sopenharmony_ci kfree(work); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic void qeth_l3_delete_ip_worker(struct work_struct *work) 210662306a36Sopenharmony_ci{ 210762306a36Sopenharmony_ci struct qeth_l3_ip_event_work *ip_work = to_ip_work(work); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci qeth_l3_modify_ip(ip_work->card, &ip_work->addr, false); 211062306a36Sopenharmony_ci kfree(work); 211162306a36Sopenharmony_ci} 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) 211462306a36Sopenharmony_ci{ 211562306a36Sopenharmony_ci if (is_vlan_dev(dev)) 211662306a36Sopenharmony_ci dev = vlan_dev_real_dev(dev); 211762306a36Sopenharmony_ci if (dev->netdev_ops == &qeth_l3_osa_netdev_ops || 211862306a36Sopenharmony_ci dev->netdev_ops == &qeth_l3_netdev_ops) 211962306a36Sopenharmony_ci return (struct qeth_card *) dev->ml_priv; 212062306a36Sopenharmony_ci return NULL; 212162306a36Sopenharmony_ci} 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_cistatic int qeth_l3_ip_event(struct notifier_block *this, 212462306a36Sopenharmony_ci unsigned long event, void *ptr) 212562306a36Sopenharmony_ci{ 212662306a36Sopenharmony_ci struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; 212762306a36Sopenharmony_ci struct net_device *dev = ifa->ifa_dev->dev; 212862306a36Sopenharmony_ci struct qeth_ipaddr addr; 212962306a36Sopenharmony_ci struct qeth_card *card; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci card = qeth_l3_get_card_from_dev(dev); 213262306a36Sopenharmony_ci if (!card) 213362306a36Sopenharmony_ci return NOTIFY_DONE; 213462306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "ipevent"); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4); 213762306a36Sopenharmony_ci addr.u.a4.addr = ifa->ifa_address; 213862306a36Sopenharmony_ci addr.u.a4.mask = ifa->ifa_mask; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci return qeth_l3_handle_ip_event(card, &addr, event); 214162306a36Sopenharmony_ci} 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_cistatic struct notifier_block qeth_l3_ip_notifier = { 214462306a36Sopenharmony_ci qeth_l3_ip_event, 214562306a36Sopenharmony_ci NULL, 214662306a36Sopenharmony_ci}; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cistatic int qeth_l3_ip6_event(struct notifier_block *this, 214962306a36Sopenharmony_ci unsigned long event, void *ptr) 215062306a36Sopenharmony_ci{ 215162306a36Sopenharmony_ci struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; 215262306a36Sopenharmony_ci struct net_device *dev = ifa->idev->dev; 215362306a36Sopenharmony_ci struct qeth_l3_ip_event_work *ip_work; 215462306a36Sopenharmony_ci struct qeth_card *card; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (event != NETDEV_UP && event != NETDEV_DOWN) 215762306a36Sopenharmony_ci return NOTIFY_DONE; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci card = qeth_l3_get_card_from_dev(dev); 216062306a36Sopenharmony_ci if (!card) 216162306a36Sopenharmony_ci return NOTIFY_DONE; 216262306a36Sopenharmony_ci QETH_CARD_TEXT(card, 3, "ip6event"); 216362306a36Sopenharmony_ci if (!qeth_is_supported(card, IPA_IPV6)) 216462306a36Sopenharmony_ci return NOTIFY_DONE; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci ip_work = kmalloc(sizeof(*ip_work), GFP_ATOMIC); 216762306a36Sopenharmony_ci if (!ip_work) 216862306a36Sopenharmony_ci return NOTIFY_DONE; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci if (event == NETDEV_UP) 217162306a36Sopenharmony_ci INIT_WORK(&ip_work->work, qeth_l3_add_ip_worker); 217262306a36Sopenharmony_ci else 217362306a36Sopenharmony_ci INIT_WORK(&ip_work->work, qeth_l3_delete_ip_worker); 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci ip_work->card = card; 217662306a36Sopenharmony_ci qeth_l3_init_ipaddr(&ip_work->addr, QETH_IP_TYPE_NORMAL, 217762306a36Sopenharmony_ci QETH_PROT_IPV6); 217862306a36Sopenharmony_ci ip_work->addr.u.a6.addr = ifa->addr; 217962306a36Sopenharmony_ci ip_work->addr.u.a6.pfxlen = ifa->prefix_len; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci queue_work(card->cmd_wq, &ip_work->work); 218262306a36Sopenharmony_ci return NOTIFY_OK; 218362306a36Sopenharmony_ci} 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_cistatic struct notifier_block qeth_l3_ip6_notifier = { 218662306a36Sopenharmony_ci qeth_l3_ip6_event, 218762306a36Sopenharmony_ci NULL, 218862306a36Sopenharmony_ci}; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_cistatic int qeth_l3_register_notifiers(void) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci int rc; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci QETH_DBF_TEXT(SETUP, 5, "regnotif"); 219562306a36Sopenharmony_ci rc = register_inetaddr_notifier(&qeth_l3_ip_notifier); 219662306a36Sopenharmony_ci if (rc) 219762306a36Sopenharmony_ci return rc; 219862306a36Sopenharmony_ci rc = register_inet6addr_notifier(&qeth_l3_ip6_notifier); 219962306a36Sopenharmony_ci if (rc) { 220062306a36Sopenharmony_ci unregister_inetaddr_notifier(&qeth_l3_ip_notifier); 220162306a36Sopenharmony_ci return rc; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci return 0; 220462306a36Sopenharmony_ci} 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic void qeth_l3_unregister_notifiers(void) 220762306a36Sopenharmony_ci{ 220862306a36Sopenharmony_ci QETH_DBF_TEXT(SETUP, 5, "unregnot"); 220962306a36Sopenharmony_ci WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier)); 221062306a36Sopenharmony_ci WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier)); 221162306a36Sopenharmony_ci} 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_cistatic int __init qeth_l3_init(void) 221462306a36Sopenharmony_ci{ 221562306a36Sopenharmony_ci pr_info("register layer 3 discipline\n"); 221662306a36Sopenharmony_ci return qeth_l3_register_notifiers(); 221762306a36Sopenharmony_ci} 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cistatic void __exit qeth_l3_exit(void) 222062306a36Sopenharmony_ci{ 222162306a36Sopenharmony_ci qeth_l3_unregister_notifiers(); 222262306a36Sopenharmony_ci pr_info("unregister layer 3 discipline\n"); 222362306a36Sopenharmony_ci} 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_cimodule_init(qeth_l3_init); 222662306a36Sopenharmony_cimodule_exit(qeth_l3_exit); 222762306a36Sopenharmony_ciMODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>"); 222862306a36Sopenharmony_ciMODULE_DESCRIPTION("qeth layer 3 discipline"); 222962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2230