1ab9953ffSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24a71df50SFrank Blaschka /*
3bbcfcdc8SFrank Blaschka * Copyright IBM Corp. 2007, 2009
44a71df50SFrank Blaschka * Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
54a71df50SFrank Blaschka * Frank Pavlic <fpavlic@de.ibm.com>,
64a71df50SFrank Blaschka * Thomas Spatzier <tspat@de.ibm.com>,
74a71df50SFrank Blaschka * Frank Blaschka <frank.blaschka@de.ibm.com>
84a71df50SFrank Blaschka */
94a71df50SFrank Blaschka
1074eacdb9SFrank Blaschka #define KMSG_COMPONENT "qeth"
1174eacdb9SFrank Blaschka #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1274eacdb9SFrank Blaschka
134a71df50SFrank Blaschka #include <linux/module.h>
144a71df50SFrank Blaschka #include <linux/moduleparam.h>
157ff0bcf6SJiri Pirko #include <linux/bitops.h>
164a71df50SFrank Blaschka #include <linux/string.h>
174a71df50SFrank Blaschka #include <linux/errno.h>
184a71df50SFrank Blaschka #include <linux/kernel.h>
194a71df50SFrank Blaschka #include <linux/etherdevice.h>
204a71df50SFrank Blaschka #include <linux/ip.h>
211f979123SJulian Wiedmann #include <linux/in.h>
22180f5131SThorsten Winkler #include <linux/inet.h>
2364ef8957SFrank Blaschka #include <linux/ipv6.h>
244a71df50SFrank Blaschka #include <linux/inetdevice.h>
254a71df50SFrank Blaschka #include <linux/igmp.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
27910a0a8fSJulian Wiedmann #include <linux/if_ether.h>
287ff0bcf6SJiri Pirko #include <linux/if_vlan.h>
29910a0a8fSJulian Wiedmann #include <linux/skbuff.h>
304a71df50SFrank Blaschka
314a71df50SFrank Blaschka #include <net/ip.h>
324a71df50SFrank Blaschka #include <net/arp.h>
3387e7597bSDavid Miller #include <net/route.h>
341f979123SJulian Wiedmann #include <net/ipv6.h>
35910a0a8fSJulian Wiedmann #include <net/ip6_route.h>
36b3332930SFrank Blaschka #include <net/iucv/af_iucv.h>
375f78e29cSLakhvich Dmitriy #include <linux/hashtable.h>
384a71df50SFrank Blaschka
394a71df50SFrank Blaschka #include "qeth_l3.h"
404a71df50SFrank Blaschka
414a71df50SFrank Blaschka static int qeth_l3_register_addr_entry(struct qeth_card *,
424a71df50SFrank Blaschka struct qeth_ipaddr *);
434a71df50SFrank Blaschka static int qeth_l3_deregister_addr_entry(struct qeth_card *,
444a71df50SFrank Blaschka struct qeth_ipaddr *);
454a71df50SFrank Blaschka
qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto,const u8 * addr,char * buf)46e6b1b7daSJulian Wiedmann int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr,
474a71df50SFrank Blaschka char *buf)
484a71df50SFrank Blaschka {
494a71df50SFrank Blaschka if (proto == QETH_PROT_IPV4)
5074c05a38SThorsten Winkler return scnprintf(buf, INET_ADDRSTRLEN, "%pI4", addr);
51e6b1b7daSJulian Wiedmann else
5274c05a38SThorsten Winkler return scnprintf(buf, INET6_ADDRSTRLEN, "%pI6", addr);
534a71df50SFrank Blaschka }
544a71df50SFrank Blaschka
qeth_l3_find_addr_by_ip(struct qeth_card * card,struct qeth_ipaddr * query)55c5c48c58SJulian Wiedmann static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
56c5c48c58SJulian Wiedmann struct qeth_ipaddr *query)
57c5c48c58SJulian Wiedmann {
581b40d4b2SJulian Wiedmann u32 key = qeth_l3_ipaddr_hash(query);
59c5c48c58SJulian Wiedmann struct qeth_ipaddr *addr;
60c5c48c58SJulian Wiedmann
61c5c48c58SJulian Wiedmann if (query->is_multicast) {
620973292fSJulian Wiedmann hash_for_each_possible(card->rx_mode_addrs, addr, hnode, key)
63c5c48c58SJulian Wiedmann if (qeth_l3_addr_match_ip(addr, query))
64c5c48c58SJulian Wiedmann return addr;
65c5c48c58SJulian Wiedmann } else {
66c5c48c58SJulian Wiedmann hash_for_each_possible(card->ip_htable, addr, hnode, key)
67c5c48c58SJulian Wiedmann if (qeth_l3_addr_match_ip(addr, query))
68c5c48c58SJulian Wiedmann return addr;
69c5c48c58SJulian Wiedmann }
70c5c48c58SJulian Wiedmann return NULL;
71c5c48c58SJulian Wiedmann }
72c5c48c58SJulian Wiedmann
qeth_l3_convert_addr_to_bits(u8 * addr,u8 * bits,int len)734a71df50SFrank Blaschka static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
744a71df50SFrank Blaschka {
754a71df50SFrank Blaschka int i, j;
764a71df50SFrank Blaschka u8 octet;
774a71df50SFrank Blaschka
784a71df50SFrank Blaschka for (i = 0; i < len; ++i) {
794a71df50SFrank Blaschka octet = addr[i];
804a71df50SFrank Blaschka for (j = 7; j >= 0; --j) {
814a71df50SFrank Blaschka bits[i*8 + j] = octet & 1;
824a71df50SFrank Blaschka octet >>= 1;
834a71df50SFrank Blaschka }
844a71df50SFrank Blaschka }
854a71df50SFrank Blaschka }
864a71df50SFrank Blaschka
qeth_l3_is_addr_covered_by_ipato(struct qeth_card * card,struct qeth_ipaddr * addr)8702f510f3SJulian Wiedmann static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
884a71df50SFrank Blaschka struct qeth_ipaddr *addr)
894a71df50SFrank Blaschka {
904a71df50SFrank Blaschka struct qeth_ipato_entry *ipatoe;
914a71df50SFrank Blaschka u8 addr_bits[128] = {0, };
924a71df50SFrank Blaschka u8 ipatoe_bits[128] = {0, };
934a71df50SFrank Blaschka int rc = 0;
944a71df50SFrank Blaschka
954a71df50SFrank Blaschka if (!card->ipato.enabled)
961a363b0dSGustavo A. R. Silva return false;
97b22d73d6SJulian Wiedmann if (addr->type != QETH_IP_TYPE_NORMAL)
981a363b0dSGustavo A. R. Silva return false;
994a71df50SFrank Blaschka
1004a71df50SFrank Blaschka qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits,
1014a71df50SFrank Blaschka (addr->proto == QETH_PROT_IPV4) ? 4 : 16);
1024a71df50SFrank Blaschka list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
1034a71df50SFrank Blaschka if (addr->proto != ipatoe->proto)
1044a71df50SFrank Blaschka continue;
1054a71df50SFrank Blaschka qeth_l3_convert_addr_to_bits(ipatoe->addr, ipatoe_bits,
1064a71df50SFrank Blaschka (ipatoe->proto == QETH_PROT_IPV4) ?
1074a71df50SFrank Blaschka 4 : 16);
108ab29c480SJulian Wiedmann rc = !memcmp(addr_bits, ipatoe_bits, ipatoe->mask_bits);
1094a71df50SFrank Blaschka if (rc)
1104a71df50SFrank Blaschka break;
1114a71df50SFrank Blaschka }
1124a71df50SFrank Blaschka /* invert? */
1134a71df50SFrank Blaschka if ((addr->proto == QETH_PROT_IPV4) && card->ipato.invert4)
1144a71df50SFrank Blaschka rc = !rc;
1154a71df50SFrank Blaschka else if ((addr->proto == QETH_PROT_IPV6) && card->ipato.invert6)
1164a71df50SFrank Blaschka rc = !rc;
1174a71df50SFrank Blaschka
1184a71df50SFrank Blaschka return rc;
1194a71df50SFrank Blaschka }
1204a71df50SFrank Blaschka
qeth_l3_delete_ip(struct qeth_card * card,struct qeth_ipaddr * tmp_addr)1211617dae2SJulian Wiedmann static int qeth_l3_delete_ip(struct qeth_card *card,
1221617dae2SJulian Wiedmann struct qeth_ipaddr *tmp_addr)
1235f78e29cSLakhvich Dmitriy {
1245f78e29cSLakhvich Dmitriy int rc = 0;
1255f78e29cSLakhvich Dmitriy struct qeth_ipaddr *addr;
1265f78e29cSLakhvich Dmitriy
1271617dae2SJulian Wiedmann if (tmp_addr->type == QETH_IP_TYPE_RXIP)
1281617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "delrxip");
1291617dae2SJulian Wiedmann else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
1301617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "delvipa");
1311617dae2SJulian Wiedmann else
1321617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "delip");
1335f78e29cSLakhvich Dmitriy
1345f78e29cSLakhvich Dmitriy if (tmp_addr->proto == QETH_PROT_IPV4)
1355f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
1364a71df50SFrank Blaschka else {
1375f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
1385f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
1395f78e29cSLakhvich Dmitriy }
1405f78e29cSLakhvich Dmitriy
141c5c48c58SJulian Wiedmann addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
142c5c48c58SJulian Wiedmann if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
1435f78e29cSLakhvich Dmitriy return -ENOENT;
1445f78e29cSLakhvich Dmitriy
1455f78e29cSLakhvich Dmitriy addr->ref_counter--;
1464964c66fSJulian Wiedmann if (addr->type == QETH_IP_TYPE_NORMAL && addr->ref_counter > 0)
1475f78e29cSLakhvich Dmitriy return rc;
1485f78e29cSLakhvich Dmitriy
14998d823abSJulian Wiedmann if (qeth_card_hw_is_reachable(card))
1505f78e29cSLakhvich Dmitriy rc = qeth_l3_deregister_addr_entry(card, addr);
1515f78e29cSLakhvich Dmitriy
1525f78e29cSLakhvich Dmitriy hash_del(&addr->hnode);
1535f78e29cSLakhvich Dmitriy kfree(addr);
1545f78e29cSLakhvich Dmitriy
1555f78e29cSLakhvich Dmitriy return rc;
1565f78e29cSLakhvich Dmitriy }
1575f78e29cSLakhvich Dmitriy
qeth_l3_add_ip(struct qeth_card * card,struct qeth_ipaddr * tmp_addr)1581617dae2SJulian Wiedmann static int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
1595f78e29cSLakhvich Dmitriy {
1605f78e29cSLakhvich Dmitriy int rc = 0;
1615f78e29cSLakhvich Dmitriy struct qeth_ipaddr *addr;
162180f5131SThorsten Winkler char buf[INET6_ADDRSTRLEN];
1635f78e29cSLakhvich Dmitriy
1641617dae2SJulian Wiedmann if (tmp_addr->type == QETH_IP_TYPE_RXIP)
1651617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "addrxip");
1661617dae2SJulian Wiedmann else if (tmp_addr->type == QETH_IP_TYPE_VIPA)
1671617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "addvipa");
1681617dae2SJulian Wiedmann else
1691617dae2SJulian Wiedmann QETH_CARD_TEXT(card, 2, "addip");
1705f78e29cSLakhvich Dmitriy
1715f78e29cSLakhvich Dmitriy if (tmp_addr->proto == QETH_PROT_IPV4)
1725f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, &tmp_addr->u.a4.addr, 4);
1735f78e29cSLakhvich Dmitriy else {
1745f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, &tmp_addr->u.a6.addr, 8);
1755f78e29cSLakhvich Dmitriy QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
1765f78e29cSLakhvich Dmitriy }
1775f78e29cSLakhvich Dmitriy
178c5c48c58SJulian Wiedmann addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
179c5c48c58SJulian Wiedmann if (addr) {
180c5c48c58SJulian Wiedmann if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
181c5c48c58SJulian Wiedmann return -EADDRINUSE;
182c5c48c58SJulian Wiedmann if (qeth_l3_addr_match_all(addr, tmp_addr)) {
183c5c48c58SJulian Wiedmann addr->ref_counter++;
184c5c48c58SJulian Wiedmann return 0;
185c5c48c58SJulian Wiedmann }
186c5c48c58SJulian Wiedmann qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
187c5c48c58SJulian Wiedmann buf);
188c5c48c58SJulian Wiedmann dev_warn(&card->gdev->dev,
189c5c48c58SJulian Wiedmann "Registering IP address %s failed\n", buf);
190c5c48c58SJulian Wiedmann return -EADDRINUSE;
191c5c48c58SJulian Wiedmann } else {
192b80c08acSJulian Wiedmann addr = kmemdup(tmp_addr, sizeof(*tmp_addr), GFP_KERNEL);
1935f78e29cSLakhvich Dmitriy if (!addr)
1945f78e29cSLakhvich Dmitriy return -ENOMEM;
1955f78e29cSLakhvich Dmitriy
196b22d73d6SJulian Wiedmann if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
197847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "tkovaddr");
198b1d5e36bSJulian Wiedmann addr->ipato = 1;
1994a71df50SFrank Blaschka }
2005f78e29cSLakhvich Dmitriy hash_add(card->ip_htable, &addr->hnode,
2015f78e29cSLakhvich Dmitriy qeth_l3_ipaddr_hash(addr));
2025f78e29cSLakhvich Dmitriy
203a7531c1cSUrsula Braun if (!qeth_card_hw_is_reachable(card)) {
204a7531c1cSUrsula Braun addr->disp_flag = QETH_DISP_ADDR_ADD;
205a7531c1cSUrsula Braun return 0;
206a7531c1cSUrsula Braun }
207a7531c1cSUrsula Braun
2085f78e29cSLakhvich Dmitriy rc = qeth_l3_register_addr_entry(card, addr);
2095f78e29cSLakhvich Dmitriy
2104b7ae122SJulian Wiedmann if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) {
2115f78e29cSLakhvich Dmitriy addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
2125f78e29cSLakhvich Dmitriy } else {
2135f78e29cSLakhvich Dmitriy hash_del(&addr->hnode);
2145f78e29cSLakhvich Dmitriy kfree(addr);
2154a71df50SFrank Blaschka }
2164a71df50SFrank Blaschka }
2174a71df50SFrank Blaschka return rc;
2184a71df50SFrank Blaschka }
2194a71df50SFrank Blaschka
qeth_l3_modify_ip(struct qeth_card * card,struct qeth_ipaddr * addr,bool add)22005a17851SJulian Wiedmann static int qeth_l3_modify_ip(struct qeth_card *card, struct qeth_ipaddr *addr,
22105a17851SJulian Wiedmann bool add)
22205a17851SJulian Wiedmann {
22305a17851SJulian Wiedmann int rc;
22405a17851SJulian Wiedmann
225df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
22605a17851SJulian Wiedmann rc = add ? qeth_l3_add_ip(card, addr) : qeth_l3_delete_ip(card, addr);
227df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
22805a17851SJulian Wiedmann
22905a17851SJulian Wiedmann return rc;
23005a17851SJulian Wiedmann }
23105a17851SJulian Wiedmann
qeth_l3_drain_rx_mode_cache(struct qeth_card * card)232d0c74825SJulian Wiedmann static void qeth_l3_drain_rx_mode_cache(struct qeth_card *card)
233d0c74825SJulian Wiedmann {
234d0c74825SJulian Wiedmann struct qeth_ipaddr *addr;
235d0c74825SJulian Wiedmann struct hlist_node *tmp;
236d0c74825SJulian Wiedmann int i;
237d0c74825SJulian Wiedmann
2380973292fSJulian Wiedmann hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) {
239d0c74825SJulian Wiedmann hash_del(&addr->hnode);
240d0c74825SJulian Wiedmann kfree(addr);
241d0c74825SJulian Wiedmann }
242d0c74825SJulian Wiedmann }
243d0c74825SJulian Wiedmann
qeth_l3_clear_ip_htable(struct qeth_card * card,int recover)2445f78e29cSLakhvich Dmitriy static void qeth_l3_clear_ip_htable(struct qeth_card *card, int recover)
2454a71df50SFrank Blaschka {
2464a71df50SFrank Blaschka struct qeth_ipaddr *addr;
2475f78e29cSLakhvich Dmitriy struct hlist_node *tmp;
2485f78e29cSLakhvich Dmitriy int i;
2494a71df50SFrank Blaschka
250847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "clearip");
2515f78e29cSLakhvich Dmitriy
252df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
2535f78e29cSLakhvich Dmitriy
2545f78e29cSLakhvich Dmitriy hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
2555f78e29cSLakhvich Dmitriy if (!recover) {
2565f78e29cSLakhvich Dmitriy hash_del(&addr->hnode);
2574a71df50SFrank Blaschka kfree(addr);
258*a1b4ed41SAlexandra Winter } else {
259*a1b4ed41SAlexandra Winter /* prepare for recovery */
2605f78e29cSLakhvich Dmitriy addr->disp_flag = QETH_DISP_ADDR_ADD;
2614a71df50SFrank Blaschka }
262*a1b4ed41SAlexandra Winter }
2634a71df50SFrank Blaschka
264df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
2655f78e29cSLakhvich Dmitriy }
2665f78e29cSLakhvich Dmitriy
qeth_l3_recover_ip(struct qeth_card * card)2675f78e29cSLakhvich Dmitriy static void qeth_l3_recover_ip(struct qeth_card *card)
2684a71df50SFrank Blaschka {
2695f78e29cSLakhvich Dmitriy struct qeth_ipaddr *addr;
2705f78e29cSLakhvich Dmitriy struct hlist_node *tmp;
2715f78e29cSLakhvich Dmitriy int i;
2725f78e29cSLakhvich Dmitriy int rc;
2734a71df50SFrank Blaschka
274a7531c1cSUrsula Braun QETH_CARD_TEXT(card, 4, "recovrip");
2754a71df50SFrank Blaschka
276df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
2774a71df50SFrank Blaschka
2785f78e29cSLakhvich Dmitriy hash_for_each_safe(card->ip_htable, i, tmp, addr, hnode) {
27998d823abSJulian Wiedmann if (addr->disp_flag == QETH_DISP_ADDR_ADD) {
2805f78e29cSLakhvich Dmitriy rc = qeth_l3_register_addr_entry(card, addr);
2815f78e29cSLakhvich Dmitriy
282*a1b4ed41SAlexandra Winter if (!rc || rc == -EADDRINUSE || rc == -ENETDOWN) {
283*a1b4ed41SAlexandra Winter /* keep it in the records */
2845f78e29cSLakhvich Dmitriy addr->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
2855f78e29cSLakhvich Dmitriy } else {
286*a1b4ed41SAlexandra Winter /* bad address */
2875f78e29cSLakhvich Dmitriy hash_del(&addr->hnode);
2885f78e29cSLakhvich Dmitriy kfree(addr);
2895f78e29cSLakhvich Dmitriy }
2905f78e29cSLakhvich Dmitriy }
2915f78e29cSLakhvich Dmitriy }
2925f78e29cSLakhvich Dmitriy
293df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
2944a71df50SFrank Blaschka }
2954a71df50SFrank Blaschka
qeth_l3_setdelip_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)2964b7ae122SJulian Wiedmann static int qeth_l3_setdelip_cb(struct qeth_card *card, struct qeth_reply *reply,
2974b7ae122SJulian Wiedmann unsigned long data)
2984b7ae122SJulian Wiedmann {
2994b7ae122SJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
3004b7ae122SJulian Wiedmann
3014b7ae122SJulian Wiedmann switch (cmd->hdr.return_code) {
3024b7ae122SJulian Wiedmann case IPA_RC_SUCCESS:
3034b7ae122SJulian Wiedmann return 0;
3044b7ae122SJulian Wiedmann case IPA_RC_DUPLICATE_IP_ADDRESS:
3054b7ae122SJulian Wiedmann return -EADDRINUSE;
3064b7ae122SJulian Wiedmann case IPA_RC_MC_ADDR_NOT_FOUND:
3074b7ae122SJulian Wiedmann return -ENOENT;
3084b7ae122SJulian Wiedmann case IPA_RC_LAN_OFFLINE:
3094b7ae122SJulian Wiedmann return -ENETDOWN;
3104b7ae122SJulian Wiedmann default:
3114b7ae122SJulian Wiedmann return -EIO;
3124b7ae122SJulian Wiedmann }
3134b7ae122SJulian Wiedmann }
3144b7ae122SJulian Wiedmann
qeth_l3_send_setdelmc(struct qeth_card * card,struct qeth_ipaddr * addr,enum qeth_ipa_cmds ipacmd)3154a71df50SFrank Blaschka static int qeth_l3_send_setdelmc(struct qeth_card *card,
316b14912ebSJulian Wiedmann struct qeth_ipaddr *addr,
317b14912ebSJulian Wiedmann enum qeth_ipa_cmds ipacmd)
3184a71df50SFrank Blaschka {
3194a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
3204a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd;
3214a71df50SFrank Blaschka
322847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setdelmc");
3234a71df50SFrank Blaschka
324a59d121dSJulian Wiedmann iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto,
325a59d121dSJulian Wiedmann IPA_DATA_SIZEOF(setdelipm));
3261aec42bcSThomas Richter if (!iob)
3271aec42bcSThomas Richter return -ENOMEM;
328ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob);
3298bf70b68SJulian Wiedmann if (addr->proto == QETH_PROT_IPV6) {
3308bf70b68SJulian Wiedmann cmd->data.setdelipm.ip = addr->u.a6.addr;
3318bf70b68SJulian Wiedmann ipv6_eth_mc_map(&addr->u.a6.addr, cmd->data.setdelipm.mac);
3328bf70b68SJulian Wiedmann } else {
3338bf70b68SJulian Wiedmann cmd->data.setdelipm.ip.s6_addr32[3] = addr->u.a4.addr;
3348bf70b68SJulian Wiedmann ip_eth_mc_map(addr->u.a4.addr, cmd->data.setdelipm.mac);
3358bf70b68SJulian Wiedmann }
3364a71df50SFrank Blaschka
3374b7ae122SJulian Wiedmann return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
3384a71df50SFrank Blaschka }
3394a71df50SFrank Blaschka
qeth_l3_set_ipv6_prefix(struct in6_addr * prefix,unsigned int len)340490df971SJulian Wiedmann static void qeth_l3_set_ipv6_prefix(struct in6_addr *prefix, unsigned int len)
3414a71df50SFrank Blaschka {
342490df971SJulian Wiedmann unsigned int i = 0;
343490df971SJulian Wiedmann
344490df971SJulian Wiedmann while (len && i < 4) {
345490df971SJulian Wiedmann int mask_len = min_t(int, len, 32);
346490df971SJulian Wiedmann
347490df971SJulian Wiedmann prefix->s6_addr32[i] = inet_make_mask(mask_len);
348490df971SJulian Wiedmann len -= mask_len;
349490df971SJulian Wiedmann i++;
3504a71df50SFrank Blaschka }
3514a71df50SFrank Blaschka }
3524a71df50SFrank Blaschka
qeth_l3_get_setdelip_flags(struct qeth_ipaddr * addr,bool set)353b1d5e36bSJulian Wiedmann static u32 qeth_l3_get_setdelip_flags(struct qeth_ipaddr *addr, bool set)
3544a71df50SFrank Blaschka {
355b1d5e36bSJulian Wiedmann switch (addr->type) {
356b1d5e36bSJulian Wiedmann case QETH_IP_TYPE_RXIP:
357b1d5e36bSJulian Wiedmann return (set) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
358b1d5e36bSJulian Wiedmann case QETH_IP_TYPE_VIPA:
359b1d5e36bSJulian Wiedmann return (set) ? QETH_IPA_SETIP_VIPA_FLAG :
360b1d5e36bSJulian Wiedmann QETH_IPA_DELIP_VIPA_FLAG;
361b1d5e36bSJulian Wiedmann default:
362b1d5e36bSJulian Wiedmann return (set && addr->ipato) ? QETH_IPA_SETIP_TAKEOVER_FLAG : 0;
363b1d5e36bSJulian Wiedmann }
364b1d5e36bSJulian Wiedmann }
365b1d5e36bSJulian Wiedmann
qeth_l3_send_setdelip(struct qeth_card * card,struct qeth_ipaddr * addr,enum qeth_ipa_cmds ipacmd)366b1d5e36bSJulian Wiedmann static int qeth_l3_send_setdelip(struct qeth_card *card,
367b1d5e36bSJulian Wiedmann struct qeth_ipaddr *addr,
368b1d5e36bSJulian Wiedmann enum qeth_ipa_cmds ipacmd)
369b1d5e36bSJulian Wiedmann {
3704a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
3714a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd;
372b1d5e36bSJulian Wiedmann u32 flags;
3734a71df50SFrank Blaschka
374847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setdelip");
3754a71df50SFrank Blaschka
376a59d121dSJulian Wiedmann iob = qeth_ipa_alloc_cmd(card, ipacmd, addr->proto,
377a59d121dSJulian Wiedmann IPA_DATA_SIZEOF(setdelip6));
3781aec42bcSThomas Richter if (!iob)
3791aec42bcSThomas Richter return -ENOMEM;
380ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob);
381b1d5e36bSJulian Wiedmann
382b1d5e36bSJulian Wiedmann flags = qeth_l3_get_setdelip_flags(addr, ipacmd == IPA_CMD_SETIP);
383b1d5e36bSJulian Wiedmann QETH_CARD_TEXT_(card, 4, "flags%02X", flags);
384b1d5e36bSJulian Wiedmann
3854a71df50SFrank Blaschka if (addr->proto == QETH_PROT_IPV6) {
386490df971SJulian Wiedmann cmd->data.setdelip6.addr = addr->u.a6.addr;
387490df971SJulian Wiedmann qeth_l3_set_ipv6_prefix(&cmd->data.setdelip6.prefix,
388490df971SJulian Wiedmann addr->u.a6.pfxlen);
3894a71df50SFrank Blaschka cmd->data.setdelip6.flags = flags;
3904a71df50SFrank Blaschka } else {
391490df971SJulian Wiedmann cmd->data.setdelip4.addr = addr->u.a4.addr;
392490df971SJulian Wiedmann cmd->data.setdelip4.mask = addr->u.a4.mask;
3934a71df50SFrank Blaschka cmd->data.setdelip4.flags = flags;
3944a71df50SFrank Blaschka }
3954a71df50SFrank Blaschka
3964b7ae122SJulian Wiedmann return qeth_send_ipa_cmd(card, iob, qeth_l3_setdelip_cb, NULL);
3974a71df50SFrank Blaschka }
3984a71df50SFrank Blaschka
qeth_l3_send_setrouting(struct qeth_card * card,enum qeth_routing_types type,enum qeth_prot_versions prot)3994a71df50SFrank Blaschka static int qeth_l3_send_setrouting(struct qeth_card *card,
4004a71df50SFrank Blaschka enum qeth_routing_types type, enum qeth_prot_versions prot)
4014a71df50SFrank Blaschka {
4024a71df50SFrank Blaschka int rc;
4034a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd;
4044a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
4054a71df50SFrank Blaschka
406847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "setroutg");
407a59d121dSJulian Wiedmann iob = qeth_ipa_alloc_cmd(card, IPA_CMD_SETRTG, prot,
408a59d121dSJulian Wiedmann IPA_DATA_SIZEOF(setrtg));
4091aec42bcSThomas Richter if (!iob)
4101aec42bcSThomas Richter return -ENOMEM;
411ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob);
4124a71df50SFrank Blaschka cmd->data.setrtg.type = (type);
4134a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
4144a71df50SFrank Blaschka
4154a71df50SFrank Blaschka return rc;
4164a71df50SFrank Blaschka }
4174a71df50SFrank Blaschka
qeth_l3_correct_routing_type(struct qeth_card * card,enum qeth_routing_types * type,enum qeth_prot_versions prot)41882e2e782SStefan Raspl static int qeth_l3_correct_routing_type(struct qeth_card *card,
4194a71df50SFrank Blaschka enum qeth_routing_types *type, enum qeth_prot_versions prot)
4204a71df50SFrank Blaschka {
421379ac99eSJulian Wiedmann if (IS_IQD(card)) {
4224a71df50SFrank Blaschka switch (*type) {
4234a71df50SFrank Blaschka case NO_ROUTER:
4244a71df50SFrank Blaschka case PRIMARY_CONNECTOR:
4254a71df50SFrank Blaschka case SECONDARY_CONNECTOR:
4264a71df50SFrank Blaschka case MULTICAST_ROUTER:
42782e2e782SStefan Raspl return 0;
4284a71df50SFrank Blaschka default:
4294a71df50SFrank Blaschka goto out_inval;
4304a71df50SFrank Blaschka }
4314a71df50SFrank Blaschka } else {
4324a71df50SFrank Blaschka switch (*type) {
4334a71df50SFrank Blaschka case NO_ROUTER:
4344a71df50SFrank Blaschka case PRIMARY_ROUTER:
4354a71df50SFrank Blaschka case SECONDARY_ROUTER:
43682e2e782SStefan Raspl return 0;
4374a71df50SFrank Blaschka case MULTICAST_ROUTER:
4384a71df50SFrank Blaschka if (qeth_is_ipafunc_supported(card, prot,
4394a71df50SFrank Blaschka IPA_OSA_MC_ROUTER))
44082e2e782SStefan Raspl return 0;
441d4e81342SGustavo A. R. Silva goto out_inval;
4424a71df50SFrank Blaschka default:
4434a71df50SFrank Blaschka goto out_inval;
4444a71df50SFrank Blaschka }
4454a71df50SFrank Blaschka }
4464a71df50SFrank Blaschka out_inval:
4474a71df50SFrank Blaschka *type = NO_ROUTER;
44882e2e782SStefan Raspl return -EINVAL;
4494a71df50SFrank Blaschka }
4504a71df50SFrank Blaschka
qeth_l3_setrouting_v4(struct qeth_card * card)4514a71df50SFrank Blaschka int qeth_l3_setrouting_v4(struct qeth_card *card)
4524a71df50SFrank Blaschka {
4534a71df50SFrank Blaschka int rc;
4544a71df50SFrank Blaschka
455847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "setrtg4");
4564a71df50SFrank Blaschka
45782e2e782SStefan Raspl rc = qeth_l3_correct_routing_type(card, &card->options.route4.type,
4584a71df50SFrank Blaschka QETH_PROT_IPV4);
45982e2e782SStefan Raspl if (rc)
46082e2e782SStefan Raspl return rc;
4614a71df50SFrank Blaschka
4624a71df50SFrank Blaschka rc = qeth_l3_send_setrouting(card, card->options.route4.type,
4634a71df50SFrank Blaschka QETH_PROT_IPV4);
4644a71df50SFrank Blaschka if (rc) {
4654a71df50SFrank Blaschka card->options.route4.type = NO_ROUTER;
466e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n",
467e19e5be8SJulian Wiedmann rc, CARD_DEVID(card));
4684a71df50SFrank Blaschka }
4694a71df50SFrank Blaschka return rc;
4704a71df50SFrank Blaschka }
4714a71df50SFrank Blaschka
qeth_l3_setrouting_v6(struct qeth_card * card)4724a71df50SFrank Blaschka int qeth_l3_setrouting_v6(struct qeth_card *card)
4734a71df50SFrank Blaschka {
4744a71df50SFrank Blaschka int rc = 0;
4754a71df50SFrank Blaschka
476847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "setrtg6");
4774a71df50SFrank Blaschka
4784a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_IPV6))
4794a71df50SFrank Blaschka return 0;
48082e2e782SStefan Raspl rc = qeth_l3_correct_routing_type(card, &card->options.route6.type,
4814a71df50SFrank Blaschka QETH_PROT_IPV6);
48282e2e782SStefan Raspl if (rc)
48382e2e782SStefan Raspl return rc;
4844a71df50SFrank Blaschka
4854a71df50SFrank Blaschka rc = qeth_l3_send_setrouting(card, card->options.route6.type,
4864a71df50SFrank Blaschka QETH_PROT_IPV6);
4874a71df50SFrank Blaschka if (rc) {
4884a71df50SFrank Blaschka card->options.route6.type = NO_ROUTER;
489e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Error (%#06x) while setting routing type on device %x. Type set to 'no router'.\n",
490e19e5be8SJulian Wiedmann rc, CARD_DEVID(card));
4914a71df50SFrank Blaschka }
4924a71df50SFrank Blaschka return rc;
4934a71df50SFrank Blaschka }
4944a71df50SFrank Blaschka
4954a71df50SFrank Blaschka /*
4964a71df50SFrank Blaschka * IP address takeover related functions
4974a71df50SFrank Blaschka */
49802f510f3SJulian Wiedmann
4997ffaef82SHeiko Carstens /*
50002f510f3SJulian Wiedmann * qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs.
50102f510f3SJulian Wiedmann *
50202f510f3SJulian Wiedmann * Caller must hold ip_lock.
50302f510f3SJulian Wiedmann */
qeth_l3_update_ipato(struct qeth_card * card)50402f510f3SJulian Wiedmann void qeth_l3_update_ipato(struct qeth_card *card)
50502f510f3SJulian Wiedmann {
50602f510f3SJulian Wiedmann struct qeth_ipaddr *addr;
50702f510f3SJulian Wiedmann unsigned int i;
50802f510f3SJulian Wiedmann
50902f510f3SJulian Wiedmann hash_for_each(card->ip_htable, i, addr, hnode) {
51002f510f3SJulian Wiedmann if (addr->type != QETH_IP_TYPE_NORMAL)
51102f510f3SJulian Wiedmann continue;
512b1d5e36bSJulian Wiedmann addr->ipato = qeth_l3_is_addr_covered_by_ipato(card, addr);
51302f510f3SJulian Wiedmann }
51402f510f3SJulian Wiedmann }
51502f510f3SJulian Wiedmann
qeth_l3_clear_ipato_list(struct qeth_card * card)5164a71df50SFrank Blaschka static void qeth_l3_clear_ipato_list(struct qeth_card *card)
5174a71df50SFrank Blaschka {
5184a71df50SFrank Blaschka struct qeth_ipato_entry *ipatoe, *tmp;
5194a71df50SFrank Blaschka
520df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
5215f78e29cSLakhvich Dmitriy
5224a71df50SFrank Blaschka list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
5234a71df50SFrank Blaschka list_del(&ipatoe->entry);
5244a71df50SFrank Blaschka kfree(ipatoe);
5254a71df50SFrank Blaschka }
5265f78e29cSLakhvich Dmitriy
52702f510f3SJulian Wiedmann qeth_l3_update_ipato(card);
528df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
5294a71df50SFrank Blaschka }
5304a71df50SFrank Blaschka
qeth_l3_add_ipato_entry(struct qeth_card * card,struct qeth_ipato_entry * new)5314a71df50SFrank Blaschka int qeth_l3_add_ipato_entry(struct qeth_card *card,
5324a71df50SFrank Blaschka struct qeth_ipato_entry *new)
5334a71df50SFrank Blaschka {
5344a71df50SFrank Blaschka struct qeth_ipato_entry *ipatoe;
5354a71df50SFrank Blaschka int rc = 0;
5364a71df50SFrank Blaschka
537847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "addipato");
5385f78e29cSLakhvich Dmitriy
539df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
5405f78e29cSLakhvich Dmitriy
5414a71df50SFrank Blaschka list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
5424a71df50SFrank Blaschka if (ipatoe->proto != new->proto)
5434a71df50SFrank Blaschka continue;
5444a71df50SFrank Blaschka if (!memcmp(ipatoe->addr, new->addr,
5454a71df50SFrank Blaschka (ipatoe->proto == QETH_PROT_IPV4) ? 4 : 16) &&
5464a71df50SFrank Blaschka (ipatoe->mask_bits == new->mask_bits)) {
5474a71df50SFrank Blaschka rc = -EEXIST;
5484a71df50SFrank Blaschka break;
5494a71df50SFrank Blaschka }
5504a71df50SFrank Blaschka }
5515f78e29cSLakhvich Dmitriy
55202f510f3SJulian Wiedmann if (!rc) {
5534a71df50SFrank Blaschka list_add_tail(&new->entry, &card->ipato.entries);
55402f510f3SJulian Wiedmann qeth_l3_update_ipato(card);
55502f510f3SJulian Wiedmann }
5564a71df50SFrank Blaschka
557df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
5585f78e29cSLakhvich Dmitriy
5594a71df50SFrank Blaschka return rc;
5604a71df50SFrank Blaschka }
5614a71df50SFrank Blaschka
qeth_l3_del_ipato_entry(struct qeth_card * card,enum qeth_prot_versions proto,u8 * addr,unsigned int mask_bits)562b9ea5250SJulian Wiedmann int qeth_l3_del_ipato_entry(struct qeth_card *card,
563b9ea5250SJulian Wiedmann enum qeth_prot_versions proto, u8 *addr,
564ab29c480SJulian Wiedmann unsigned int mask_bits)
5654a71df50SFrank Blaschka {
5664a71df50SFrank Blaschka struct qeth_ipato_entry *ipatoe, *tmp;
567b9ea5250SJulian Wiedmann int rc = -ENOENT;
5684a71df50SFrank Blaschka
569847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "delipato");
5705f78e29cSLakhvich Dmitriy
571df2a2a52SJulian Wiedmann mutex_lock(&card->ip_lock);
5725f78e29cSLakhvich Dmitriy
5734a71df50SFrank Blaschka list_for_each_entry_safe(ipatoe, tmp, &card->ipato.entries, entry) {
5744a71df50SFrank Blaschka if (ipatoe->proto != proto)
5754a71df50SFrank Blaschka continue;
5764a71df50SFrank Blaschka if (!memcmp(ipatoe->addr, addr,
5774a71df50SFrank Blaschka (proto == QETH_PROT_IPV4) ? 4 : 16) &&
5784a71df50SFrank Blaschka (ipatoe->mask_bits == mask_bits)) {
5794a71df50SFrank Blaschka list_del(&ipatoe->entry);
58002f510f3SJulian Wiedmann qeth_l3_update_ipato(card);
5814a71df50SFrank Blaschka kfree(ipatoe);
582b9ea5250SJulian Wiedmann rc = 0;
5834a71df50SFrank Blaschka }
5844a71df50SFrank Blaschka }
5855f78e29cSLakhvich Dmitriy
586df2a2a52SJulian Wiedmann mutex_unlock(&card->ip_lock);
5872390166aSJulian Wiedmann
588b9ea5250SJulian Wiedmann return rc;
5894a71df50SFrank Blaschka }
5904a71df50SFrank Blaschka
qeth_l3_modify_rxip_vipa(struct qeth_card * card,bool add,const u8 * ip,enum qeth_ip_types type,enum qeth_prot_versions proto)5911617dae2SJulian Wiedmann int qeth_l3_modify_rxip_vipa(struct qeth_card *card, bool add, const u8 *ip,
5921617dae2SJulian Wiedmann enum qeth_ip_types type,
5931617dae2SJulian Wiedmann enum qeth_prot_versions proto)
5944a71df50SFrank Blaschka {
5951617dae2SJulian Wiedmann struct qeth_ipaddr addr;
5964a71df50SFrank Blaschka
5971617dae2SJulian Wiedmann qeth_l3_init_ipaddr(&addr, type, proto);
5981617dae2SJulian Wiedmann if (proto == QETH_PROT_IPV4)
5991617dae2SJulian Wiedmann memcpy(&addr.u.a4.addr, ip, 4);
6001617dae2SJulian Wiedmann else
6011617dae2SJulian Wiedmann memcpy(&addr.u.a6.addr, ip, 16);
6025f78e29cSLakhvich Dmitriy
603f3380b1eSJulian Wiedmann return qeth_l3_modify_ip(card, &addr, add);
6044a71df50SFrank Blaschka }
6054a71df50SFrank Blaschka
qeth_l3_modify_hsuid(struct qeth_card * card,bool add)6061617dae2SJulian Wiedmann int qeth_l3_modify_hsuid(struct qeth_card *card, bool add)
6074a71df50SFrank Blaschka {
6081617dae2SJulian Wiedmann struct qeth_ipaddr addr;
60905a17851SJulian Wiedmann unsigned int i;
6104a71df50SFrank Blaschka
6111617dae2SJulian Wiedmann qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
6121617dae2SJulian Wiedmann addr.u.a6.addr.s6_addr[0] = 0xfe;
6131617dae2SJulian Wiedmann addr.u.a6.addr.s6_addr[1] = 0x80;
6141617dae2SJulian Wiedmann for (i = 0; i < 8; i++)
6151617dae2SJulian Wiedmann addr.u.a6.addr.s6_addr[8+i] = card->options.hsuid[i];
6165f78e29cSLakhvich Dmitriy
61705a17851SJulian Wiedmann return qeth_l3_modify_ip(card, &addr, add);
6184a71df50SFrank Blaschka }
6194a71df50SFrank Blaschka
qeth_l3_register_addr_entry(struct qeth_card * card,struct qeth_ipaddr * addr)6204a71df50SFrank Blaschka static int qeth_l3_register_addr_entry(struct qeth_card *card,
6214a71df50SFrank Blaschka struct qeth_ipaddr *addr)
6224a71df50SFrank Blaschka {
6234a71df50SFrank Blaschka char buf[50];
6244a71df50SFrank Blaschka int rc = 0;
6254a71df50SFrank Blaschka int cnt = 3;
6264a71df50SFrank Blaschka
627bd74a7f9SJulian Wiedmann if (card->options.sniffer)
628bd74a7f9SJulian Wiedmann return 0;
6295f78e29cSLakhvich Dmitriy
6304a71df50SFrank Blaschka if (addr->proto == QETH_PROT_IPV4) {
631847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "setaddr4");
632847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
6334a71df50SFrank Blaschka } else if (addr->proto == QETH_PROT_IPV6) {
634847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "setaddr6");
635847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
636847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
6374a71df50SFrank Blaschka } else {
638847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "setaddr?");
639847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
6404a71df50SFrank Blaschka }
6414a71df50SFrank Blaschka do {
6424a71df50SFrank Blaschka if (addr->is_multicast)
6434a71df50SFrank Blaschka rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_SETIPM);
6444a71df50SFrank Blaschka else
645b1d5e36bSJulian Wiedmann rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_SETIP);
6464a71df50SFrank Blaschka if (rc)
647847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "failed");
6484a71df50SFrank Blaschka } while ((--cnt > 0) && rc);
6494a71df50SFrank Blaschka if (rc) {
650847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "FAILED");
6514a71df50SFrank Blaschka qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
65274eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
65374eacdb9SFrank Blaschka "Registering IP address %s failed\n", buf);
6544a71df50SFrank Blaschka }
6554a71df50SFrank Blaschka return rc;
6564a71df50SFrank Blaschka }
6574a71df50SFrank Blaschka
qeth_l3_deregister_addr_entry(struct qeth_card * card,struct qeth_ipaddr * addr)6584a71df50SFrank Blaschka static int qeth_l3_deregister_addr_entry(struct qeth_card *card,
6594a71df50SFrank Blaschka struct qeth_ipaddr *addr)
6604a71df50SFrank Blaschka {
6614a71df50SFrank Blaschka int rc = 0;
6624a71df50SFrank Blaschka
663bd74a7f9SJulian Wiedmann if (card->options.sniffer)
664bd74a7f9SJulian Wiedmann return 0;
665bd74a7f9SJulian Wiedmann
6664a71df50SFrank Blaschka if (addr->proto == QETH_PROT_IPV4) {
667847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "deladdr4");
668847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, &addr->u.a4.addr, sizeof(int));
6694a71df50SFrank Blaschka } else if (addr->proto == QETH_PROT_IPV6) {
670847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "deladdr6");
671847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, &addr->u.a6.addr, 8);
672847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, ((char *)&addr->u.a6.addr) + 8, 8);
6734a71df50SFrank Blaschka } else {
674847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "deladdr?");
675847a50fdSCarsten Otte QETH_CARD_HEX(card, 3, addr, sizeof(struct qeth_ipaddr));
6764a71df50SFrank Blaschka }
6774a71df50SFrank Blaschka if (addr->is_multicast)
6784a71df50SFrank Blaschka rc = qeth_l3_send_setdelmc(card, addr, IPA_CMD_DELIPM);
6794a71df50SFrank Blaschka else
680b1d5e36bSJulian Wiedmann rc = qeth_l3_send_setdelip(card, addr, IPA_CMD_DELIP);
681c4cef07cSFrank Blaschka if (rc)
682847a50fdSCarsten Otte QETH_CARD_TEXT(card, 2, "failed");
6834a71df50SFrank Blaschka
6844a71df50SFrank Blaschka return rc;
6854a71df50SFrank Blaschka }
6864a71df50SFrank Blaschka
qeth_l3_setadapter_parms(struct qeth_card * card)6874a71df50SFrank Blaschka static int qeth_l3_setadapter_parms(struct qeth_card *card)
6884a71df50SFrank Blaschka {
689699d3fe5SJulian Wiedmann int rc = 0;
6904a71df50SFrank Blaschka
69157a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "setadprm");
6924a71df50SFrank Blaschka
6934a71df50SFrank Blaschka if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
6944a71df50SFrank Blaschka rc = qeth_setadpparms_change_macaddr(card);
6954a71df50SFrank Blaschka if (rc)
69674eacdb9SFrank Blaschka dev_warn(&card->gdev->dev, "Reading the adapter MAC"
6976ea2fde1SHeiko Carstens " address failed\n");
6984a71df50SFrank Blaschka }
6994a71df50SFrank Blaschka
7004a71df50SFrank Blaschka return rc;
7014a71df50SFrank Blaschka }
7024a71df50SFrank Blaschka
qeth_l3_start_ipa_arp_processing(struct qeth_card * card)7034a71df50SFrank Blaschka static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
7044a71df50SFrank Blaschka {
7054a71df50SFrank Blaschka int rc;
7064a71df50SFrank Blaschka
707847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "ipaarp");
7084a71df50SFrank Blaschka
7094a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
71074eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
71174eacdb9SFrank Blaschka "ARP processing not supported on %s!\n",
71284c91482SJulian Wiedmann netdev_name(card->dev));
7134a71df50SFrank Blaschka return 0;
7144a71df50SFrank Blaschka }
7154d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
7161c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
7174a71df50SFrank Blaschka if (rc) {
71874eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
71974eacdb9SFrank Blaschka "Starting ARP processing support for %s failed\n",
72084c91482SJulian Wiedmann netdev_name(card->dev));
7214a71df50SFrank Blaschka }
7224a71df50SFrank Blaschka return rc;
7234a71df50SFrank Blaschka }
7244a71df50SFrank Blaschka
qeth_l3_start_ipa_source_mac(struct qeth_card * card)7254a71df50SFrank Blaschka static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
7264a71df50SFrank Blaschka {
7274a71df50SFrank Blaschka int rc;
7284a71df50SFrank Blaschka
729847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "stsrcmac");
7304a71df50SFrank Blaschka
7314a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
73274eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
733fe94e2e0SUrsula Braun "Inbound source MAC-address not supported on %s\n",
73484c91482SJulian Wiedmann netdev_name(card->dev));
7354a71df50SFrank Blaschka return -EOPNOTSUPP;
7364a71df50SFrank Blaschka }
7374a71df50SFrank Blaschka
7384d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
7391c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
7404a71df50SFrank Blaschka if (rc)
74174eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
742fe94e2e0SUrsula Braun "Starting source MAC-address support for %s failed\n",
74384c91482SJulian Wiedmann netdev_name(card->dev));
7444a71df50SFrank Blaschka return rc;
7454a71df50SFrank Blaschka }
7464a71df50SFrank Blaschka
qeth_l3_start_ipa_vlan(struct qeth_card * card)7474a71df50SFrank Blaschka static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
7484a71df50SFrank Blaschka {
7494a71df50SFrank Blaschka int rc = 0;
7504a71df50SFrank Blaschka
751847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "strtvlan");
7524a71df50SFrank Blaschka
7534a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
75474eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
75584c91482SJulian Wiedmann "VLAN not supported on %s\n", netdev_name(card->dev));
7564a71df50SFrank Blaschka return -EOPNOTSUPP;
7574a71df50SFrank Blaschka }
7584a71df50SFrank Blaschka
7594d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
7601c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
7614a71df50SFrank Blaschka if (rc) {
76274eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
76374eacdb9SFrank Blaschka "Starting VLAN support for %s failed\n",
76484c91482SJulian Wiedmann netdev_name(card->dev));
7654a71df50SFrank Blaschka } else {
76674eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "VLAN enabled\n");
7674a71df50SFrank Blaschka }
7684a71df50SFrank Blaschka return rc;
7694a71df50SFrank Blaschka }
7704a71df50SFrank Blaschka
qeth_l3_start_ipa_multicast(struct qeth_card * card)7714a71df50SFrank Blaschka static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
7724a71df50SFrank Blaschka {
7734a71df50SFrank Blaschka int rc;
7744a71df50SFrank Blaschka
775847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "stmcast");
7764a71df50SFrank Blaschka
7774a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_MULTICASTING)) {
77874eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
77974eacdb9SFrank Blaschka "Multicast not supported on %s\n",
78084c91482SJulian Wiedmann netdev_name(card->dev));
7814a71df50SFrank Blaschka return -EOPNOTSUPP;
7824a71df50SFrank Blaschka }
7834a71df50SFrank Blaschka
7844d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
7851c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
7864a71df50SFrank Blaschka if (rc) {
78774eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
78874eacdb9SFrank Blaschka "Starting multicast support for %s failed\n",
78984c91482SJulian Wiedmann netdev_name(card->dev));
7904a71df50SFrank Blaschka } else {
79174eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "Multicast enabled\n");
7924a71df50SFrank Blaschka card->dev->flags |= IFF_MULTICAST;
7934a71df50SFrank Blaschka }
7944a71df50SFrank Blaschka return rc;
7954a71df50SFrank Blaschka }
7964a71df50SFrank Blaschka
qeth_l3_softsetup_ipv6(struct qeth_card * card)7974a71df50SFrank Blaschka static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
7984a71df50SFrank Blaschka {
7991c696c89SJulian Wiedmann u32 ipv6_data = 3;
8004a71df50SFrank Blaschka int rc;
8014a71df50SFrank Blaschka
802847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "softipv6");
8034a71df50SFrank Blaschka
804379ac99eSJulian Wiedmann if (IS_IQD(card))
80523274596SJulian Wiedmann goto out;
80623274596SJulian Wiedmann
8071c696c89SJulian Wiedmann rc = qeth_send_simple_setassparms(card, IPA_IPV6, IPA_CMD_ASS_START,
8081c696c89SJulian Wiedmann &ipv6_data);
8094a71df50SFrank Blaschka if (rc) {
81074eacdb9SFrank Blaschka dev_err(&card->gdev->dev,
81174eacdb9SFrank Blaschka "Activating IPv6 support for %s failed\n",
81284c91482SJulian Wiedmann netdev_name(card->dev));
8134a71df50SFrank Blaschka return rc;
8144a71df50SFrank Blaschka }
8151c696c89SJulian Wiedmann rc = qeth_send_simple_setassparms_v6(card, IPA_IPV6, IPA_CMD_ASS_START,
8161c696c89SJulian Wiedmann NULL);
8174a71df50SFrank Blaschka if (rc) {
81874eacdb9SFrank Blaschka dev_err(&card->gdev->dev,
81974eacdb9SFrank Blaschka "Activating IPv6 support for %s failed\n",
82084c91482SJulian Wiedmann netdev_name(card->dev));
8214a71df50SFrank Blaschka return rc;
8224a71df50SFrank Blaschka }
823a8155b00SKittipon Meesompop rc = qeth_send_simple_setassparms_v6(card, IPA_PASSTHRU,
8241c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
8254a71df50SFrank Blaschka if (rc) {
82674eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
82774eacdb9SFrank Blaschka "Enabling the passthrough mode for %s failed\n",
82884c91482SJulian Wiedmann netdev_name(card->dev));
8294a71df50SFrank Blaschka return rc;
8304a71df50SFrank Blaschka }
8314a71df50SFrank Blaschka out:
83274eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "IPV6 enabled\n");
8334a71df50SFrank Blaschka return 0;
8344a71df50SFrank Blaschka }
8354a71df50SFrank Blaschka
qeth_l3_start_ipa_ipv6(struct qeth_card * card)8364a71df50SFrank Blaschka static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
8374a71df50SFrank Blaschka {
838847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "strtipv6");
8394a71df50SFrank Blaschka
8404a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_IPV6)) {
84174eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
84284c91482SJulian Wiedmann "IPv6 not supported on %s\n", netdev_name(card->dev));
8434a71df50SFrank Blaschka return 0;
8444a71df50SFrank Blaschka }
845c062204fSJulian Wiedmann return qeth_l3_softsetup_ipv6(card);
8464a71df50SFrank Blaschka }
8474a71df50SFrank Blaschka
qeth_l3_start_ipa_broadcast(struct qeth_card * card)8484a71df50SFrank Blaschka static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
8494a71df50SFrank Blaschka {
8501c696c89SJulian Wiedmann u32 filter_data = 1;
8514a71df50SFrank Blaschka int rc;
8524a71df50SFrank Blaschka
853847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "stbrdcst");
8544a71df50SFrank Blaschka card->info.broadcast_capable = 0;
8554a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_FILTERING)) {
85674eacdb9SFrank Blaschka dev_info(&card->gdev->dev,
85774eacdb9SFrank Blaschka "Broadcast not supported on %s\n",
85884c91482SJulian Wiedmann netdev_name(card->dev));
8594a71df50SFrank Blaschka rc = -EOPNOTSUPP;
8604a71df50SFrank Blaschka goto out;
8614a71df50SFrank Blaschka }
8624d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
8631c696c89SJulian Wiedmann IPA_CMD_ASS_START, NULL);
8644a71df50SFrank Blaschka if (rc) {
86584c91482SJulian Wiedmann dev_warn(&card->gdev->dev,
86684c91482SJulian Wiedmann "Enabling broadcast filtering for %s failed\n",
86784c91482SJulian Wiedmann netdev_name(card->dev));
8684a71df50SFrank Blaschka goto out;
8694a71df50SFrank Blaschka }
8704a71df50SFrank Blaschka
8714d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
8721c696c89SJulian Wiedmann IPA_CMD_ASS_CONFIGURE, &filter_data);
8734a71df50SFrank Blaschka if (rc) {
87474eacdb9SFrank Blaschka dev_warn(&card->gdev->dev,
87574eacdb9SFrank Blaschka "Setting up broadcast filtering for %s failed\n",
87684c91482SJulian Wiedmann netdev_name(card->dev));
8774a71df50SFrank Blaschka goto out;
8784a71df50SFrank Blaschka }
8794a71df50SFrank Blaschka card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
88074eacdb9SFrank Blaschka dev_info(&card->gdev->dev, "Broadcast enabled\n");
8814d7def2aSThomas Richter rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
8821c696c89SJulian Wiedmann IPA_CMD_ASS_ENABLE, &filter_data);
8834a71df50SFrank Blaschka if (rc) {
88484c91482SJulian Wiedmann dev_warn(&card->gdev->dev,
88584c91482SJulian Wiedmann "Setting up broadcast echo filtering for %s failed\n",
88684c91482SJulian Wiedmann netdev_name(card->dev));
8874a71df50SFrank Blaschka goto out;
8884a71df50SFrank Blaschka }
8894a71df50SFrank Blaschka card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
8904a71df50SFrank Blaschka out:
8914a71df50SFrank Blaschka if (card->info.broadcast_capable)
8924a71df50SFrank Blaschka card->dev->flags |= IFF_BROADCAST;
8934a71df50SFrank Blaschka else
8944a71df50SFrank Blaschka card->dev->flags &= ~IFF_BROADCAST;
8954a71df50SFrank Blaschka return rc;
8964a71df50SFrank Blaschka }
8974a71df50SFrank Blaschka
qeth_l3_start_ipassists(struct qeth_card * card)89817d9723eSJulian Wiedmann static void qeth_l3_start_ipassists(struct qeth_card *card)
8994a71df50SFrank Blaschka {
900847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "strtipas");
901d64ecc22SEinar Lueck
9024a71df50SFrank Blaschka qeth_l3_start_ipa_arp_processing(card); /* go on*/
9034a71df50SFrank Blaschka qeth_l3_start_ipa_source_mac(card); /* go on*/
9044a71df50SFrank Blaschka qeth_l3_start_ipa_vlan(card); /* go on*/
9054a71df50SFrank Blaschka qeth_l3_start_ipa_multicast(card); /* go on*/
9064a71df50SFrank Blaschka qeth_l3_start_ipa_ipv6(card); /* go on*/
9074a71df50SFrank Blaschka qeth_l3_start_ipa_broadcast(card); /* go on*/
9084a71df50SFrank Blaschka }
9094a71df50SFrank Blaschka
qeth_l3_iqd_read_initial_mac_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)9104a71df50SFrank Blaschka static int qeth_l3_iqd_read_initial_mac_cb(struct qeth_card *card,
9114a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data)
9124a71df50SFrank Blaschka {
913742d4d40SJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
9144a71df50SFrank Blaschka
915742d4d40SJulian Wiedmann if (cmd->hdr.return_code)
916742d4d40SJulian Wiedmann return -EIO;
91713bf8295SJulian Wiedmann if (!is_valid_ether_addr(cmd->data.create_destroy_addr.mac_addr))
91813bf8295SJulian Wiedmann return -EADDRNOTAVAIL;
919742d4d40SJulian Wiedmann
920978bb0aeSJakub Kicinski eth_hw_addr_set(card->dev, cmd->data.create_destroy_addr.mac_addr);
9214a71df50SFrank Blaschka return 0;
9224a71df50SFrank Blaschka }
9234a71df50SFrank Blaschka
qeth_l3_iqd_read_initial_mac(struct qeth_card * card)9244a71df50SFrank Blaschka static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card)
9254a71df50SFrank Blaschka {
9264a71df50SFrank Blaschka int rc = 0;
9274a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
9284a71df50SFrank Blaschka
92957a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "hsrmac");
9304a71df50SFrank Blaschka
931a59d121dSJulian Wiedmann iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6,
932a59d121dSJulian Wiedmann IPA_DATA_SIZEOF(create_destroy_addr));
9331aec42bcSThomas Richter if (!iob)
9341aec42bcSThomas Richter return -ENOMEM;
9354a71df50SFrank Blaschka
9364a71df50SFrank Blaschka rc = qeth_send_ipa_cmd(card, iob, qeth_l3_iqd_read_initial_mac_cb,
9374a71df50SFrank Blaschka NULL);
9384a71df50SFrank Blaschka return rc;
9394a71df50SFrank Blaschka }
9404a71df50SFrank Blaschka
qeth_l3_get_unique_id_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)9414a71df50SFrank Blaschka static int qeth_l3_get_unique_id_cb(struct qeth_card *card,
9424a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data)
9434a71df50SFrank Blaschka {
944742d4d40SJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
9458ec1e247SJulian Wiedmann u16 *uid = reply->param;
9464a71df50SFrank Blaschka
947742d4d40SJulian Wiedmann if (cmd->hdr.return_code == 0) {
9488ec1e247SJulian Wiedmann *uid = cmd->data.create_destroy_addr.uid;
949742d4d40SJulian Wiedmann return 0;
950742d4d40SJulian Wiedmann }
951742d4d40SJulian Wiedmann
952742d4d40SJulian Wiedmann dev_warn(&card->gdev->dev, "The network adapter failed to generate a unique ID\n");
953742d4d40SJulian Wiedmann return -EIO;
9544a71df50SFrank Blaschka }
9554a71df50SFrank Blaschka
qeth_l3_get_unique_id(struct qeth_card * card,u16 uid)9568ec1e247SJulian Wiedmann static u16 qeth_l3_get_unique_id(struct qeth_card *card, u16 uid)
9574a71df50SFrank Blaschka {
9584a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
9594a71df50SFrank Blaschka
96057a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "guniqeid");
9614a71df50SFrank Blaschka
9628ec1e247SJulian Wiedmann if (!qeth_is_supported(card, IPA_IPV6))
9638ec1e247SJulian Wiedmann goto out;
9644a71df50SFrank Blaschka
965a59d121dSJulian Wiedmann iob = qeth_ipa_alloc_cmd(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6,
966a59d121dSJulian Wiedmann IPA_DATA_SIZEOF(create_destroy_addr));
9671aec42bcSThomas Richter if (!iob)
9688ec1e247SJulian Wiedmann goto out;
9694a71df50SFrank Blaschka
9708ec1e247SJulian Wiedmann __ipa_cmd(iob)->data.create_destroy_addr.uid = uid;
9718ec1e247SJulian Wiedmann qeth_send_ipa_cmd(card, iob, qeth_l3_get_unique_id_cb, &uid);
9728ec1e247SJulian Wiedmann
9738ec1e247SJulian Wiedmann out:
9748ec1e247SJulian Wiedmann return uid;
9754a71df50SFrank Blaschka }
9764a71df50SFrank Blaschka
97776b11f8eSUrsula Braun static int
qeth_diags_trace_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)97876b11f8eSUrsula Braun qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
97976b11f8eSUrsula Braun unsigned long data)
98076b11f8eSUrsula Braun {
98176b11f8eSUrsula Braun struct qeth_ipa_cmd *cmd;
98276b11f8eSUrsula Braun __u16 rc;
98376b11f8eSUrsula Braun
98457a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "diastrcb");
98576b11f8eSUrsula Braun
98676b11f8eSUrsula Braun cmd = (struct qeth_ipa_cmd *)data;
98776b11f8eSUrsula Braun rc = cmd->hdr.return_code;
988a959189aSUrsula Braun if (rc)
989847a50fdSCarsten Otte QETH_CARD_TEXT_(card, 2, "dxter%x", rc);
990a959189aSUrsula Braun switch (cmd->data.diagass.action) {
991a959189aSUrsula Braun case QETH_DIAGS_CMD_TRACE_QUERY:
992a959189aSUrsula Braun break;
993a959189aSUrsula Braun case QETH_DIAGS_CMD_TRACE_DISABLE:
99476b11f8eSUrsula Braun switch (rc) {
995a959189aSUrsula Braun case 0:
996a959189aSUrsula Braun case IPA_RC_INVALID_SUBCMD:
997a959189aSUrsula Braun card->info.promisc_mode = SET_PROMISC_MODE_OFF;
998a959189aSUrsula Braun dev_info(&card->gdev->dev, "The HiperSockets network "
999a959189aSUrsula Braun "traffic analyzer is deactivated\n");
1000a959189aSUrsula Braun break;
1001a959189aSUrsula Braun default:
1002a959189aSUrsula Braun break;
1003a959189aSUrsula Braun }
1004a959189aSUrsula Braun break;
1005a959189aSUrsula Braun case QETH_DIAGS_CMD_TRACE_ENABLE:
1006a959189aSUrsula Braun switch (rc) {
1007a959189aSUrsula Braun case 0:
1008a959189aSUrsula Braun card->info.promisc_mode = SET_PROMISC_MODE_ON;
1009a959189aSUrsula Braun dev_info(&card->gdev->dev, "The HiperSockets network "
1010a959189aSUrsula Braun "traffic analyzer is activated\n");
1011a959189aSUrsula Braun break;
101276b11f8eSUrsula Braun case IPA_RC_HARDWARE_AUTH_ERROR:
101376b11f8eSUrsula Braun dev_warn(&card->gdev->dev, "The device is not "
1014a959189aSUrsula Braun "authorized to run as a HiperSockets network "
1015a959189aSUrsula Braun "traffic analyzer\n");
101676b11f8eSUrsula Braun break;
101776b11f8eSUrsula Braun case IPA_RC_TRACE_ALREADY_ACTIVE:
101876b11f8eSUrsula Braun dev_warn(&card->gdev->dev, "A HiperSockets "
101976b11f8eSUrsula Braun "network traffic analyzer is already "
102076b11f8eSUrsula Braun "active in the HiperSockets LAN\n");
102176b11f8eSUrsula Braun break;
102276b11f8eSUrsula Braun default:
102376b11f8eSUrsula Braun break;
102476b11f8eSUrsula Braun }
102576b11f8eSUrsula Braun break;
102676b11f8eSUrsula Braun default:
1027e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Unknown sniffer action (%#06x) on device %x\n",
1028e19e5be8SJulian Wiedmann cmd->data.diagass.action, CARD_DEVID(card));
102976b11f8eSUrsula Braun }
103076b11f8eSUrsula Braun
1031742d4d40SJulian Wiedmann return rc ? -EIO : 0;
103276b11f8eSUrsula Braun }
103376b11f8eSUrsula Braun
103476b11f8eSUrsula Braun static int
qeth_diags_trace(struct qeth_card * card,enum qeth_diags_trace_cmds diags_cmd)103576b11f8eSUrsula Braun qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
103676b11f8eSUrsula Braun {
103776b11f8eSUrsula Braun struct qeth_cmd_buffer *iob;
103876b11f8eSUrsula Braun struct qeth_ipa_cmd *cmd;
103976b11f8eSUrsula Braun
104057a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "diagtrac");
104176b11f8eSUrsula Braun
10425cfbe10aSJulian Wiedmann iob = qeth_get_diag_cmd(card, QETH_DIAGS_CMD_TRACE, 0);
10431aec42bcSThomas Richter if (!iob)
10441aec42bcSThomas Richter return -ENOMEM;
1045ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob);
104676b11f8eSUrsula Braun cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
104776b11f8eSUrsula Braun cmd->data.diagass.action = diags_cmd;
104876b11f8eSUrsula Braun return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
104976b11f8eSUrsula Braun }
105076b11f8eSUrsula Braun
qeth_l3_add_mcast_rtnl(struct net_device * dev,int vid,void * arg)10518659c189SJulian Wiedmann static int qeth_l3_add_mcast_rtnl(struct net_device *dev, int vid, void *arg)
10525f78e29cSLakhvich Dmitriy {
10538659c189SJulian Wiedmann struct qeth_card *card = arg;
1054611abe51SJulian Wiedmann struct inet6_dev *in6_dev;
1055611abe51SJulian Wiedmann struct in_device *in4_dev;
1056611abe51SJulian Wiedmann struct qeth_ipaddr *ipm;
1057b80c08acSJulian Wiedmann struct qeth_ipaddr tmp;
10584a71df50SFrank Blaschka struct ip_mc_list *im4;
1059611abe51SJulian Wiedmann struct ifmcaddr6 *im6;
10604a71df50SFrank Blaschka
1061847a50fdSCarsten Otte QETH_CARD_TEXT(card, 4, "addmc");
10625f78e29cSLakhvich Dmitriy
1063611abe51SJulian Wiedmann if (!dev || !(dev->flags & IFF_UP))
1064611abe51SJulian Wiedmann goto out;
1065611abe51SJulian Wiedmann
10668659c189SJulian Wiedmann in4_dev = __in_dev_get_rtnl(dev);
1067611abe51SJulian Wiedmann if (!in4_dev)
1068611abe51SJulian Wiedmann goto walk_ipv6;
10695f78e29cSLakhvich Dmitriy
1070b80c08acSJulian Wiedmann qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4);
1071b80c08acSJulian Wiedmann tmp.disp_flag = QETH_DISP_ADDR_ADD;
1072b80c08acSJulian Wiedmann tmp.is_multicast = 1;
1073b80c08acSJulian Wiedmann
10748659c189SJulian Wiedmann for (im4 = rtnl_dereference(in4_dev->mc_list); im4 != NULL;
10758659c189SJulian Wiedmann im4 = rtnl_dereference(im4->next_rcu)) {
1076b80c08acSJulian Wiedmann tmp.u.a4.addr = im4->multiaddr;
10775f78e29cSLakhvich Dmitriy
1078b80c08acSJulian Wiedmann ipm = qeth_l3_find_addr_by_ip(card, &tmp);
10795f78e29cSLakhvich Dmitriy if (ipm) {
1080c5c48c58SJulian Wiedmann /* for mcast, by-IP match means full match */
10815f78e29cSLakhvich Dmitriy ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
1082b80c08acSJulian Wiedmann continue;
1083b80c08acSJulian Wiedmann }
1084b80c08acSJulian Wiedmann
1085b80c08acSJulian Wiedmann ipm = kmemdup(&tmp, sizeof(tmp), GFP_KERNEL);
10864a71df50SFrank Blaschka if (!ipm)
10874a71df50SFrank Blaschka continue;
10888bf70b68SJulian Wiedmann
10890973292fSJulian Wiedmann hash_add(card->rx_mode_addrs, &ipm->hnode,
1090b80c08acSJulian Wiedmann qeth_l3_ipaddr_hash(ipm));
10914a71df50SFrank Blaschka }
10924a71df50SFrank Blaschka
1093611abe51SJulian Wiedmann walk_ipv6:
1094611abe51SJulian Wiedmann if (!qeth_is_supported(card, IPA_IPV6))
1095611abe51SJulian Wiedmann goto out;
10965f78e29cSLakhvich Dmitriy
1097611abe51SJulian Wiedmann in6_dev = __in6_dev_get(dev);
1098611abe51SJulian Wiedmann if (!in6_dev)
1099611abe51SJulian Wiedmann goto out;
11004a71df50SFrank Blaschka
1101b80c08acSJulian Wiedmann qeth_l3_init_ipaddr(&tmp, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV6);
1102b80c08acSJulian Wiedmann tmp.disp_flag = QETH_DISP_ADDR_ADD;
1103b80c08acSJulian Wiedmann tmp.is_multicast = 1;
11045f78e29cSLakhvich Dmitriy
110588e2ca30STaehee Yoo for (im6 = rtnl_dereference(in6_dev->mc_list);
110688e2ca30STaehee Yoo im6;
110788e2ca30STaehee Yoo im6 = rtnl_dereference(im6->next)) {
1108b80c08acSJulian Wiedmann tmp.u.a6.addr = im6->mca_addr;
11095f78e29cSLakhvich Dmitriy
1110b80c08acSJulian Wiedmann ipm = qeth_l3_find_addr_by_ip(card, &tmp);
11115f78e29cSLakhvich Dmitriy if (ipm) {
1112c5c48c58SJulian Wiedmann /* for mcast, by-IP match means full match */
11135f78e29cSLakhvich Dmitriy ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
11145f78e29cSLakhvich Dmitriy continue;
11155f78e29cSLakhvich Dmitriy }
11165f78e29cSLakhvich Dmitriy
1117b80c08acSJulian Wiedmann ipm = kmemdup(&tmp, sizeof(tmp), GFP_ATOMIC);
11184a71df50SFrank Blaschka if (!ipm)
11194a71df50SFrank Blaschka continue;
11205f78e29cSLakhvich Dmitriy
11210973292fSJulian Wiedmann hash_add(card->rx_mode_addrs, &ipm->hnode,
11220973292fSJulian Wiedmann qeth_l3_ipaddr_hash(ipm));
11235f78e29cSLakhvich Dmitriy
11244a71df50SFrank Blaschka }
1125ddf28100SJulian Wiedmann
1126611abe51SJulian Wiedmann out:
11278659c189SJulian Wiedmann return 0;
11284a71df50SFrank Blaschka }
11294a71df50SFrank Blaschka
qeth_l3_set_promisc_mode(struct qeth_card * card)113059b757a9SJulian Wiedmann static void qeth_l3_set_promisc_mode(struct qeth_card *card)
113176b11f8eSUrsula Braun {
113259b757a9SJulian Wiedmann bool enable = card->dev->flags & IFF_PROMISC;
113376b11f8eSUrsula Braun
113459b757a9SJulian Wiedmann if (card->info.promisc_mode == enable)
113576b11f8eSUrsula Braun return;
113676b11f8eSUrsula Braun
1137379ac99eSJulian Wiedmann if (IS_VM_NIC(card)) { /* Guestlan trace */
113876b11f8eSUrsula Braun if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
113959b757a9SJulian Wiedmann qeth_setadp_promisc_mode(card, enable);
114076b11f8eSUrsula Braun } else if (card->options.sniffer && /* HiperSockets trace */
114176b11f8eSUrsula Braun qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
114259b757a9SJulian Wiedmann if (enable) {
1143847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "+promisc");
114476b11f8eSUrsula Braun qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
114576b11f8eSUrsula Braun } else {
1146847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "-promisc");
114776b11f8eSUrsula Braun qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
114876b11f8eSUrsula Braun }
114976b11f8eSUrsula Braun }
115076b11f8eSUrsula Braun }
115176b11f8eSUrsula Braun
qeth_l3_rx_mode_work(struct work_struct * work)1152d0c74825SJulian Wiedmann static void qeth_l3_rx_mode_work(struct work_struct *work)
11534a71df50SFrank Blaschka {
1154d0c74825SJulian Wiedmann struct qeth_card *card = container_of(work, struct qeth_card,
1155d0c74825SJulian Wiedmann rx_mode_work);
115600c163f1SJulian Wiedmann struct qeth_ipaddr *addr;
115700c163f1SJulian Wiedmann struct hlist_node *tmp;
115800c163f1SJulian Wiedmann int i, rc;
11594a71df50SFrank Blaschka
1160847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "setmulti");
1161e6e771b3SJulian Wiedmann
116276b11f8eSUrsula Braun if (!card->options.sniffer) {
11638659c189SJulian Wiedmann rtnl_lock();
11648659c189SJulian Wiedmann qeth_l3_add_mcast_rtnl(card->dev, 0, card);
1165611abe51SJulian Wiedmann if (qeth_is_supported(card, IPA_FULL_VLAN))
11668659c189SJulian Wiedmann vlan_for_each(card->dev, qeth_l3_add_mcast_rtnl, card);
11678659c189SJulian Wiedmann rtnl_unlock();
116800c163f1SJulian Wiedmann
11690973292fSJulian Wiedmann hash_for_each_safe(card->rx_mode_addrs, i, tmp, addr, hnode) {
117000c163f1SJulian Wiedmann switch (addr->disp_flag) {
117100c163f1SJulian Wiedmann case QETH_DISP_ADDR_DELETE:
117200c163f1SJulian Wiedmann rc = qeth_l3_deregister_addr_entry(card, addr);
11734b7ae122SJulian Wiedmann if (!rc || rc == -ENOENT) {
117400c163f1SJulian Wiedmann hash_del(&addr->hnode);
117500c163f1SJulian Wiedmann kfree(addr);
117600c163f1SJulian Wiedmann }
117700c163f1SJulian Wiedmann break;
117800c163f1SJulian Wiedmann case QETH_DISP_ADDR_ADD:
117900c163f1SJulian Wiedmann rc = qeth_l3_register_addr_entry(card, addr);
11804b7ae122SJulian Wiedmann if (rc && rc != -ENETDOWN) {
118100c163f1SJulian Wiedmann hash_del(&addr->hnode);
118200c163f1SJulian Wiedmann kfree(addr);
118300c163f1SJulian Wiedmann break;
118400c163f1SJulian Wiedmann }
1185df561f66SGustavo A. R. Silva fallthrough;
118600c163f1SJulian Wiedmann default:
118700c163f1SJulian Wiedmann /* for next call to set_rx_mode(): */
118800c163f1SJulian Wiedmann addr->disp_flag = QETH_DISP_ADDR_DELETE;
118900c163f1SJulian Wiedmann }
119000c163f1SJulian Wiedmann }
119176b11f8eSUrsula Braun }
119259b757a9SJulian Wiedmann
119359b757a9SJulian Wiedmann qeth_l3_set_promisc_mode(card);
11944a71df50SFrank Blaschka }
11954a71df50SFrank Blaschka
qeth_l3_arp_makerc(u16 rc)1196742d4d40SJulian Wiedmann static int qeth_l3_arp_makerc(u16 rc)
11974a71df50SFrank Blaschka {
1198e19e5be8SJulian Wiedmann switch (rc) {
1199e19e5be8SJulian Wiedmann case IPA_RC_SUCCESS:
1200e19e5be8SJulian Wiedmann return 0;
12014a71df50SFrank Blaschka case QETH_IPA_ARP_RC_NOTSUPP:
12024a71df50SFrank Blaschka case QETH_IPA_ARP_RC_Q_NOTSUPP:
1203e19e5be8SJulian Wiedmann return -EOPNOTSUPP;
1204e19e5be8SJulian Wiedmann case QETH_IPA_ARP_RC_OUT_OF_RANGE:
1205e19e5be8SJulian Wiedmann return -EINVAL;
12064a71df50SFrank Blaschka case QETH_IPA_ARP_RC_Q_NO_DATA:
1207e19e5be8SJulian Wiedmann return -ENOENT;
12084a71df50SFrank Blaschka default:
1209e19e5be8SJulian Wiedmann return -EIO;
12104a71df50SFrank Blaschka }
12114a71df50SFrank Blaschka }
12124a71df50SFrank Blaschka
qeth_l3_arp_cmd_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)1213742d4d40SJulian Wiedmann static int qeth_l3_arp_cmd_cb(struct qeth_card *card, struct qeth_reply *reply,
1214742d4d40SJulian Wiedmann unsigned long data)
1215742d4d40SJulian Wiedmann {
1216742d4d40SJulian Wiedmann struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data;
1217742d4d40SJulian Wiedmann
1218742d4d40SJulian Wiedmann qeth_setassparms_cb(card, reply, data);
1219742d4d40SJulian Wiedmann return qeth_l3_arp_makerc(cmd->hdr.return_code);
1220742d4d40SJulian Wiedmann }
1221742d4d40SJulian Wiedmann
qeth_l3_arp_set_no_entries(struct qeth_card * card,int no_entries)12224a71df50SFrank Blaschka static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
12234a71df50SFrank Blaschka {
1224742d4d40SJulian Wiedmann struct qeth_cmd_buffer *iob;
12254a71df50SFrank Blaschka int rc;
12264a71df50SFrank Blaschka
1227847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "arpstnoe");
12284a71df50SFrank Blaschka
12294a71df50SFrank Blaschka /*
12304a71df50SFrank Blaschka * currently GuestLAN only supports the ARP assist function
12314a71df50SFrank Blaschka * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
12324a71df50SFrank Blaschka * thus we say EOPNOTSUPP for this ARP function
12334a71df50SFrank Blaschka */
1234379ac99eSJulian Wiedmann if (IS_VM_NIC(card))
12354a71df50SFrank Blaschka return -EOPNOTSUPP;
12364a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
12374a71df50SFrank Blaschka return -EOPNOTSUPP;
12384a71df50SFrank Blaschka }
1239742d4d40SJulian Wiedmann
1240742d4d40SJulian Wiedmann iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
1241b9150461SJulian Wiedmann IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
1242b9150461SJulian Wiedmann SETASS_DATA_SIZEOF(flags_32bit),
1243742d4d40SJulian Wiedmann QETH_PROT_IPV4);
1244742d4d40SJulian Wiedmann if (!iob)
1245742d4d40SJulian Wiedmann return -ENOMEM;
1246742d4d40SJulian Wiedmann
1247742d4d40SJulian Wiedmann __ipa_cmd(iob)->data.setassparms.data.flags_32bit = (u32) no_entries;
1248742d4d40SJulian Wiedmann rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
1249e19e5be8SJulian Wiedmann if (rc)
1250e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on device %x: %#x\n",
1251e19e5be8SJulian Wiedmann CARD_DEVID(card), rc);
1252742d4d40SJulian Wiedmann return rc;
12534a71df50SFrank Blaschka }
12544a71df50SFrank Blaschka
get_arp_entry_size(struct qeth_card * card,struct qeth_arp_query_data * qdata,struct qeth_arp_entrytype * type,__u8 strip_entries)1255d0ddf30fSEinar Lueck static __u32 get_arp_entry_size(struct qeth_card *card,
1256d0ddf30fSEinar Lueck struct qeth_arp_query_data *qdata,
1257d0ddf30fSEinar Lueck struct qeth_arp_entrytype *type, __u8 strip_entries)
12584a71df50SFrank Blaschka {
1259d0ddf30fSEinar Lueck __u32 rc;
1260d0ddf30fSEinar Lueck __u8 is_hsi;
12614a71df50SFrank Blaschka
1262d0ddf30fSEinar Lueck is_hsi = qdata->reply_bits == 5;
1263d0ddf30fSEinar Lueck if (type->ip == QETHARP_IP_ADDR_V4) {
1264d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "arpev4");
1265d0ddf30fSEinar Lueck if (strip_entries) {
1266d0ddf30fSEinar Lueck rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5_short) :
1267d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry7_short);
1268d0ddf30fSEinar Lueck } else {
1269d0ddf30fSEinar Lueck rc = is_hsi ? sizeof(struct qeth_arp_qi_entry5) :
1270d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry7);
12714a71df50SFrank Blaschka }
1272d0ddf30fSEinar Lueck } else if (type->ip == QETHARP_IP_ADDR_V6) {
1273d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "arpev6");
1274d0ddf30fSEinar Lueck if (strip_entries) {
1275d0ddf30fSEinar Lueck rc = is_hsi ?
1276d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry5_short_ipv6) :
1277d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry7_short_ipv6);
1278d0ddf30fSEinar Lueck } else {
1279d0ddf30fSEinar Lueck rc = is_hsi ?
1280d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry5_ipv6) :
1281d0ddf30fSEinar Lueck sizeof(struct qeth_arp_qi_entry7_ipv6);
1282d0ddf30fSEinar Lueck }
1283d0ddf30fSEinar Lueck } else {
1284d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "arpinv");
1285d0ddf30fSEinar Lueck rc = 0;
1286d0ddf30fSEinar Lueck }
1287d0ddf30fSEinar Lueck
1288d0ddf30fSEinar Lueck return rc;
1289d0ddf30fSEinar Lueck }
1290d0ddf30fSEinar Lueck
arpentry_matches_prot(struct qeth_arp_entrytype * type,__u16 prot)1291d0ddf30fSEinar Lueck static int arpentry_matches_prot(struct qeth_arp_entrytype *type, __u16 prot)
1292d0ddf30fSEinar Lueck {
1293d0ddf30fSEinar Lueck return (type->ip == QETHARP_IP_ADDR_V4 && prot == QETH_PROT_IPV4) ||
1294d0ddf30fSEinar Lueck (type->ip == QETHARP_IP_ADDR_V6 && prot == QETH_PROT_IPV6);
12954a71df50SFrank Blaschka }
12964a71df50SFrank Blaschka
qeth_l3_arp_query_cb(struct qeth_card * card,struct qeth_reply * reply,unsigned long data)12974a71df50SFrank Blaschka static int qeth_l3_arp_query_cb(struct qeth_card *card,
12984a71df50SFrank Blaschka struct qeth_reply *reply, unsigned long data)
12994a71df50SFrank Blaschka {
13004a71df50SFrank Blaschka struct qeth_ipa_cmd *cmd;
13014a71df50SFrank Blaschka struct qeth_arp_query_data *qdata;
13024a71df50SFrank Blaschka struct qeth_arp_query_info *qinfo;
1303d0ddf30fSEinar Lueck int e;
1304d0ddf30fSEinar Lueck int entrybytes_done;
1305d0ddf30fSEinar Lueck int stripped_bytes;
1306d0ddf30fSEinar Lueck __u8 do_strip_entries;
13074a71df50SFrank Blaschka
1308d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 3, "arpquecb");
13094a71df50SFrank Blaschka
13104a71df50SFrank Blaschka qinfo = (struct qeth_arp_query_info *) reply->param;
13114a71df50SFrank Blaschka cmd = (struct qeth_ipa_cmd *) data;
1312d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.prot_version);
13134a71df50SFrank Blaschka if (cmd->hdr.return_code) {
1314d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "arpcberr");
1315d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
13164b7ae122SJulian Wiedmann return qeth_l3_arp_makerc(cmd->hdr.return_code);
13174a71df50SFrank Blaschka }
13184a71df50SFrank Blaschka if (cmd->data.setassparms.hdr.return_code) {
13194a71df50SFrank Blaschka cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
1320d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "setaperr");
1321d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "%i", cmd->hdr.return_code);
13224b7ae122SJulian Wiedmann return qeth_l3_arp_makerc(cmd->hdr.return_code);
13234a71df50SFrank Blaschka }
13244a71df50SFrank Blaschka qdata = &cmd->data.setassparms.data.query_arp;
1325d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "anoen%i", qdata->no_entries);
1326d0ddf30fSEinar Lueck
1327d0ddf30fSEinar Lueck do_strip_entries = (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) > 0;
1328d0ddf30fSEinar Lueck stripped_bytes = do_strip_entries ? QETH_QARP_MEDIASPECIFIC_BYTES : 0;
1329d0ddf30fSEinar Lueck entrybytes_done = 0;
1330d0ddf30fSEinar Lueck for (e = 0; e < qdata->no_entries; ++e) {
1331d0ddf30fSEinar Lueck char *cur_entry;
1332d0ddf30fSEinar Lueck __u32 esize;
1333d0ddf30fSEinar Lueck struct qeth_arp_entrytype *etype;
1334d0ddf30fSEinar Lueck
1335d0ddf30fSEinar Lueck cur_entry = &qdata->data + entrybytes_done;
1336d0ddf30fSEinar Lueck etype = &((struct qeth_arp_qi_entry5 *) cur_entry)->type;
1337d0ddf30fSEinar Lueck if (!arpentry_matches_prot(etype, cmd->hdr.prot_version)) {
1338d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "pmis");
1339d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "%i", etype->ip);
13404a71df50SFrank Blaschka break;
13414a71df50SFrank Blaschka }
1342d0ddf30fSEinar Lueck esize = get_arp_entry_size(card, qdata, etype,
1343d0ddf30fSEinar Lueck do_strip_entries);
1344d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 5, "esz%i", esize);
1345d0ddf30fSEinar Lueck if (!esize)
1346d0ddf30fSEinar Lueck break;
1347d0ddf30fSEinar Lueck
1348d0ddf30fSEinar Lueck if ((qinfo->udata_len - qinfo->udata_offset) < esize) {
13494b7ae122SJulian Wiedmann QETH_CARD_TEXT_(card, 4, "qaer3%i", -ENOSPC);
13504b7ae122SJulian Wiedmann memset(qinfo->udata, 0, 4);
13514b7ae122SJulian Wiedmann return -ENOSPC;
13524a71df50SFrank Blaschka }
13534a71df50SFrank Blaschka
13544a71df50SFrank Blaschka memcpy(qinfo->udata + qinfo->udata_offset,
1355d0ddf30fSEinar Lueck &qdata->data + entrybytes_done + stripped_bytes,
1356d0ddf30fSEinar Lueck esize);
1357d0ddf30fSEinar Lueck entrybytes_done += esize + stripped_bytes;
1358d0ddf30fSEinar Lueck qinfo->udata_offset += esize;
1359d0ddf30fSEinar Lueck ++qinfo->no_entries;
1360d0ddf30fSEinar Lueck }
13614a71df50SFrank Blaschka /* check if all replies received ... */
13624a71df50SFrank Blaschka if (cmd->data.setassparms.hdr.seq_no <
13634a71df50SFrank Blaschka cmd->data.setassparms.hdr.number_of_replies)
13644a71df50SFrank Blaschka return 1;
1365d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "nove%i", qinfo->no_entries);
13664a71df50SFrank Blaschka memcpy(qinfo->udata, &qinfo->no_entries, 4);
13674a71df50SFrank Blaschka /* keep STRIP_ENTRIES flag so the user program can distinguish
13684a71df50SFrank Blaschka * stripped entries from normal ones */
13694a71df50SFrank Blaschka if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
13704a71df50SFrank Blaschka qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
13714a71df50SFrank Blaschka memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET, &qdata->reply_bits, 2);
1372d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 4, "rc%i", 0);
13734a71df50SFrank Blaschka return 0;
13744a71df50SFrank Blaschka }
13754a71df50SFrank Blaschka
qeth_l3_query_arp_cache_info(struct qeth_card * card,enum qeth_prot_versions prot,struct qeth_arp_query_info * qinfo)1376d0ddf30fSEinar Lueck static int qeth_l3_query_arp_cache_info(struct qeth_card *card,
1377d0ddf30fSEinar Lueck enum qeth_prot_versions prot,
1378d0ddf30fSEinar Lueck struct qeth_arp_query_info *qinfo)
13794a71df50SFrank Blaschka {
13804a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
1381d0ddf30fSEinar Lueck struct qeth_ipa_cmd *cmd;
13824a71df50SFrank Blaschka int rc;
13834a71df50SFrank Blaschka
1384d0ddf30fSEinar Lueck QETH_CARD_TEXT_(card, 3, "qarpipv%i", prot);
1385d0ddf30fSEinar Lueck
1386b475e316SThomas Richter iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
1387d0ddf30fSEinar Lueck IPA_CMD_ASS_ARP_QUERY_INFO,
1388b9150461SJulian Wiedmann SETASS_DATA_SIZEOF(query_arp), prot);
13891aec42bcSThomas Richter if (!iob)
13901aec42bcSThomas Richter return -ENOMEM;
1391ff5caa7aSJulian Wiedmann cmd = __ipa_cmd(iob);
1392d0ddf30fSEinar Lueck cmd->data.setassparms.data.query_arp.request_bits = 0x000F;
139384dbea46SJulian Wiedmann rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_query_cb, qinfo);
1394e19e5be8SJulian Wiedmann if (rc)
1395e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Error while querying ARP cache on device %x: %#x\n",
1396e19e5be8SJulian Wiedmann CARD_DEVID(card), rc);
13974b7ae122SJulian Wiedmann return rc;
1398d0ddf30fSEinar Lueck }
1399d0ddf30fSEinar Lueck
qeth_l3_arp_query(struct qeth_card * card,char __user * udata)1400d0ddf30fSEinar Lueck static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
1401d0ddf30fSEinar Lueck {
1402d0ddf30fSEinar Lueck struct qeth_arp_query_info qinfo = {0, };
1403d0ddf30fSEinar Lueck int rc;
1404d0ddf30fSEinar Lueck
1405847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "arpquery");
14064a71df50SFrank Blaschka
14074a71df50SFrank Blaschka if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
14084a71df50SFrank Blaschka IPA_ARP_PROCESSING)) {
1409d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 3, "arpqnsup");
1410d0ddf30fSEinar Lueck rc = -EOPNOTSUPP;
1411d0ddf30fSEinar Lueck goto out;
14124a71df50SFrank Blaschka }
14134a71df50SFrank Blaschka /* get size of userspace buffer and mask_bits -> 6 bytes */
1414d0ddf30fSEinar Lueck if (copy_from_user(&qinfo, udata, 6)) {
1415d0ddf30fSEinar Lueck rc = -EFAULT;
1416d0ddf30fSEinar Lueck goto out;
1417d0ddf30fSEinar Lueck }
14184a71df50SFrank Blaschka qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL);
1419d0ddf30fSEinar Lueck if (!qinfo.udata) {
1420d0ddf30fSEinar Lueck rc = -ENOMEM;
1421d0ddf30fSEinar Lueck goto out;
1422d0ddf30fSEinar Lueck }
14234a71df50SFrank Blaschka qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
1424d0ddf30fSEinar Lueck rc = qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV4, &qinfo);
14254a71df50SFrank Blaschka if (rc) {
14264a71df50SFrank Blaschka if (copy_to_user(udata, qinfo.udata, 4))
14274a71df50SFrank Blaschka rc = -EFAULT;
1428d0ddf30fSEinar Lueck goto free_and_out;
142977a83ed1SSebastian Ott }
1430d0ddf30fSEinar Lueck if (qinfo.mask_bits & QETH_QARP_WITH_IPV6) {
1431d0ddf30fSEinar Lueck /* fails in case of GuestLAN QDIO mode */
143277a83ed1SSebastian Ott qeth_l3_query_arp_cache_info(card, QETH_PROT_IPV6, &qinfo);
14334a71df50SFrank Blaschka }
1434d0ddf30fSEinar Lueck if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) {
1435d0ddf30fSEinar Lueck QETH_CARD_TEXT(card, 4, "qactf");
1436d0ddf30fSEinar Lueck rc = -EFAULT;
1437d0ddf30fSEinar Lueck goto free_and_out;
1438d0ddf30fSEinar Lueck }
14398a593148SThomas Richter QETH_CARD_TEXT(card, 4, "qacts");
144077a83ed1SSebastian Ott
1441d0ddf30fSEinar Lueck free_and_out:
14424a71df50SFrank Blaschka kfree(qinfo.udata);
1443d0ddf30fSEinar Lueck out:
14444a71df50SFrank Blaschka return rc;
14454a71df50SFrank Blaschka }
14464a71df50SFrank Blaschka
qeth_l3_arp_modify_entry(struct qeth_card * card,struct qeth_arp_cache_entry * entry,enum qeth_arp_process_subcmds arp_cmd)1447125d7d30SJulian Wiedmann static int qeth_l3_arp_modify_entry(struct qeth_card *card,
1448125d7d30SJulian Wiedmann struct qeth_arp_cache_entry *entry,
1449125d7d30SJulian Wiedmann enum qeth_arp_process_subcmds arp_cmd)
14504a71df50SFrank Blaschka {
1451125d7d30SJulian Wiedmann struct qeth_arp_cache_entry *cmd_entry;
14524a71df50SFrank Blaschka struct qeth_cmd_buffer *iob;
14534a71df50SFrank Blaschka int rc;
14544a71df50SFrank Blaschka
1455125d7d30SJulian Wiedmann if (arp_cmd == IPA_CMD_ASS_ARP_ADD_ENTRY)
1456125d7d30SJulian Wiedmann QETH_CARD_TEXT(card, 3, "arpadd");
1457125d7d30SJulian Wiedmann else
1458125d7d30SJulian Wiedmann QETH_CARD_TEXT(card, 3, "arpdel");
14594a71df50SFrank Blaschka
14604a71df50SFrank Blaschka /*
14614a71df50SFrank Blaschka * currently GuestLAN only supports the ARP assist function
14624a71df50SFrank Blaschka * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
14634a71df50SFrank Blaschka * thus we say EOPNOTSUPP for this ARP function
14644a71df50SFrank Blaschka */
1465379ac99eSJulian Wiedmann if (IS_VM_NIC(card))
14664a71df50SFrank Blaschka return -EOPNOTSUPP;
14674a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
14684a71df50SFrank Blaschka return -EOPNOTSUPP;
14694a71df50SFrank Blaschka }
14704a71df50SFrank Blaschka
1471125d7d30SJulian Wiedmann iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING, arp_cmd,
1472b9150461SJulian Wiedmann SETASS_DATA_SIZEOF(arp_entry),
1473b9150461SJulian Wiedmann QETH_PROT_IPV4);
14741aec42bcSThomas Richter if (!iob)
14751aec42bcSThomas Richter return -ENOMEM;
1476125d7d30SJulian Wiedmann
1477125d7d30SJulian Wiedmann cmd_entry = &__ipa_cmd(iob)->data.setassparms.data.arp_entry;
1478125d7d30SJulian Wiedmann ether_addr_copy(cmd_entry->macaddr, entry->macaddr);
1479125d7d30SJulian Wiedmann memcpy(cmd_entry->ipaddr, entry->ipaddr, 4);
1480742d4d40SJulian Wiedmann rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
1481e19e5be8SJulian Wiedmann if (rc)
1482125d7d30SJulian Wiedmann QETH_DBF_MESSAGE(2, "Could not modify (cmd: %#x) ARP entry on device %x: %#x\n",
1483125d7d30SJulian Wiedmann arp_cmd, CARD_DEVID(card), rc);
1484742d4d40SJulian Wiedmann return rc;
14854a71df50SFrank Blaschka }
14864a71df50SFrank Blaschka
qeth_l3_arp_flush_cache(struct qeth_card * card)14874a71df50SFrank Blaschka static int qeth_l3_arp_flush_cache(struct qeth_card *card)
14884a71df50SFrank Blaschka {
1489742d4d40SJulian Wiedmann struct qeth_cmd_buffer *iob;
14904a71df50SFrank Blaschka int rc;
14914a71df50SFrank Blaschka
1492847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "arpflush");
14934a71df50SFrank Blaschka
14944a71df50SFrank Blaschka /*
14954a71df50SFrank Blaschka * currently GuestLAN only supports the ARP assist function
14964a71df50SFrank Blaschka * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
14974a71df50SFrank Blaschka * thus we say EOPNOTSUPP for this ARP function
14984a71df50SFrank Blaschka */
1499379ac99eSJulian Wiedmann if (IS_VM_NIC(card) || IS_IQD(card))
15004a71df50SFrank Blaschka return -EOPNOTSUPP;
15014a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
15024a71df50SFrank Blaschka return -EOPNOTSUPP;
15034a71df50SFrank Blaschka }
1504742d4d40SJulian Wiedmann
1505742d4d40SJulian Wiedmann iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
1506742d4d40SJulian Wiedmann IPA_CMD_ASS_ARP_FLUSH_CACHE, 0,
1507742d4d40SJulian Wiedmann QETH_PROT_IPV4);
1508742d4d40SJulian Wiedmann if (!iob)
1509742d4d40SJulian Wiedmann return -ENOMEM;
1510742d4d40SJulian Wiedmann
1511742d4d40SJulian Wiedmann rc = qeth_send_ipa_cmd(card, iob, qeth_l3_arp_cmd_cb, NULL);
1512e19e5be8SJulian Wiedmann if (rc)
1513e19e5be8SJulian Wiedmann QETH_DBF_MESSAGE(2, "Could not flush ARP cache on device %x: %#x\n",
1514e19e5be8SJulian Wiedmann CARD_DEVID(card), rc);
1515742d4d40SJulian Wiedmann return rc;
15164a71df50SFrank Blaschka }
15174a71df50SFrank Blaschka
qeth_l3_ndo_siocdevprivate(struct net_device * dev,struct ifreq * rq,void __user * data,int cmd)15182decb0b7SJulian Wiedmann static int qeth_l3_ndo_siocdevprivate(struct net_device *dev, struct ifreq *rq,
15192decb0b7SJulian Wiedmann void __user *data, int cmd)
15204a71df50SFrank Blaschka {
1521509e2562SHeiko Carstens struct qeth_card *card = dev->ml_priv;
15224a71df50SFrank Blaschka struct qeth_arp_cache_entry arp_entry;
1523125d7d30SJulian Wiedmann enum qeth_arp_process_subcmds arp_cmd;
15244a71df50SFrank Blaschka int rc = 0;
15254a71df50SFrank Blaschka
15264a71df50SFrank Blaschka switch (cmd) {
15274a71df50SFrank Blaschka case SIOC_QETH_ARP_SET_NO_ENTRIES:
15284a71df50SFrank Blaschka if (!capable(CAP_NET_ADMIN)) {
15294a71df50SFrank Blaschka rc = -EPERM;
15304a71df50SFrank Blaschka break;
15314a71df50SFrank Blaschka }
15324a71df50SFrank Blaschka rc = qeth_l3_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
15334a71df50SFrank Blaschka break;
15344a71df50SFrank Blaschka case SIOC_QETH_ARP_QUERY_INFO:
15354a71df50SFrank Blaschka if (!capable(CAP_NET_ADMIN)) {
15364a71df50SFrank Blaschka rc = -EPERM;
15374a71df50SFrank Blaschka break;
15384a71df50SFrank Blaschka }
153918787eeeSArnd Bergmann rc = qeth_l3_arp_query(card, data);
15404a71df50SFrank Blaschka break;
15414a71df50SFrank Blaschka case SIOC_QETH_ARP_ADD_ENTRY:
15424a71df50SFrank Blaschka case SIOC_QETH_ARP_REMOVE_ENTRY:
1543125d7d30SJulian Wiedmann if (!capable(CAP_NET_ADMIN))
1544125d7d30SJulian Wiedmann return -EPERM;
154518787eeeSArnd Bergmann if (copy_from_user(&arp_entry, data, sizeof(arp_entry)))
1546125d7d30SJulian Wiedmann return -EFAULT;
1547125d7d30SJulian Wiedmann
1548125d7d30SJulian Wiedmann arp_cmd = (cmd == SIOC_QETH_ARP_ADD_ENTRY) ?
1549125d7d30SJulian Wiedmann IPA_CMD_ASS_ARP_ADD_ENTRY :
1550125d7d30SJulian Wiedmann IPA_CMD_ASS_ARP_REMOVE_ENTRY;
1551125d7d30SJulian Wiedmann return qeth_l3_arp_modify_entry(card, &arp_entry, arp_cmd);
15524a71df50SFrank Blaschka case SIOC_QETH_ARP_FLUSH_CACHE:
15534a71df50SFrank Blaschka if (!capable(CAP_NET_ADMIN)) {
15544a71df50SFrank Blaschka rc = -EPERM;
15554a71df50SFrank Blaschka break;
15564a71df50SFrank Blaschka }
15574a71df50SFrank Blaschka rc = qeth_l3_arp_flush_cache(card);
15584a71df50SFrank Blaschka break;
15594a71df50SFrank Blaschka default:
15602decb0b7SJulian Wiedmann rc = qeth_siocdevprivate(dev, rq, data, cmd);
15614a71df50SFrank Blaschka }
15624a71df50SFrank Blaschka return rc;
15634a71df50SFrank Blaschka }
15644a71df50SFrank Blaschka
qeth_l3_get_cast_type_rcu(struct sk_buff * skb,struct dst_entry * dst,__be16 proto)1565980f4568SJulian Wiedmann static int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst,
156617f3a8b5SJulian Wiedmann __be16 proto)
1567ce73e10eSKlaus-Dieter Wacker {
156869cce1d1SDavid S. Miller struct neighbour *n = NULL;
1569ce73e10eSKlaus-Dieter Wacker
157069cce1d1SDavid S. Miller if (dst)
157124db1ba8SDavid S. Miller n = dst_neigh_lookup_skb(dst, skb);
15720cd6783dSJulian Wiedmann
157369cce1d1SDavid S. Miller if (n) {
15741f979123SJulian Wiedmann int cast_type = n->type;
15751f979123SJulian Wiedmann
157624db1ba8SDavid S. Miller neigh_release(n);
1577ce73e10eSKlaus-Dieter Wacker if ((cast_type == RTN_BROADCAST) ||
1578ce73e10eSKlaus-Dieter Wacker (cast_type == RTN_MULTICAST) ||
1579ce73e10eSKlaus-Dieter Wacker (cast_type == RTN_ANYCAST))
1580ce73e10eSKlaus-Dieter Wacker return cast_type;
158186c0cdb9SJulian Wiedmann return RTN_UNICAST;
1582ce73e10eSKlaus-Dieter Wacker }
15831d36cb47SFrank Blaschka
15841f979123SJulian Wiedmann /* no neighbour (eg AF_PACKET), fall back to target's IP address ... */
158517f3a8b5SJulian Wiedmann switch (proto) {
158617f3a8b5SJulian Wiedmann case htons(ETH_P_IP):
158772c87976SJulian Wiedmann if (ipv4_is_lbcast(ip_hdr(skb)->daddr))
158872c87976SJulian Wiedmann return RTN_BROADCAST;
15891f979123SJulian Wiedmann return ipv4_is_multicast(ip_hdr(skb)->daddr) ?
159086c0cdb9SJulian Wiedmann RTN_MULTICAST : RTN_UNICAST;
159117f3a8b5SJulian Wiedmann case htons(ETH_P_IPV6):
1592f13ade19SJulian Wiedmann return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ?
1593f13ade19SJulian Wiedmann RTN_MULTICAST : RTN_UNICAST;
1594a667fee1SJulian Wiedmann case htons(ETH_P_AF_IUCV):
1595a667fee1SJulian Wiedmann return RTN_UNICAST;
1596f13ade19SJulian Wiedmann default:
1597a667fee1SJulian Wiedmann /* OSA only: ... and MAC address */
159858aa2491SJulian Wiedmann return qeth_get_ether_cast_type(skb);
1599ce73e10eSKlaus-Dieter Wacker }
1600f13ade19SJulian Wiedmann }
1601ce73e10eSKlaus-Dieter Wacker
qeth_l3_get_cast_type(struct sk_buff * skb,__be16 proto)1602c61dff3cSJulian Wiedmann static int qeth_l3_get_cast_type(struct sk_buff *skb, __be16 proto)
1603980f4568SJulian Wiedmann {
1604980f4568SJulian Wiedmann struct dst_entry *dst;
1605980f4568SJulian Wiedmann int cast_type;
1606980f4568SJulian Wiedmann
1607980f4568SJulian Wiedmann rcu_read_lock();
160817f3a8b5SJulian Wiedmann dst = qeth_dst_check_rcu(skb, proto);
160917f3a8b5SJulian Wiedmann cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto);
1610980f4568SJulian Wiedmann rcu_read_unlock();
1611980f4568SJulian Wiedmann
1612980f4568SJulian Wiedmann return cast_type;
1613980f4568SJulian Wiedmann }
1614980f4568SJulian Wiedmann
qeth_l3_cast_type_to_flag(int cast_type)1615910a0a8fSJulian Wiedmann static u8 qeth_l3_cast_type_to_flag(int cast_type)
1616910a0a8fSJulian Wiedmann {
1617910a0a8fSJulian Wiedmann if (cast_type == RTN_MULTICAST)
1618910a0a8fSJulian Wiedmann return QETH_CAST_MULTICAST;
1619910a0a8fSJulian Wiedmann if (cast_type == RTN_ANYCAST)
1620910a0a8fSJulian Wiedmann return QETH_CAST_ANYCAST;
1621910a0a8fSJulian Wiedmann if (cast_type == RTN_BROADCAST)
1622910a0a8fSJulian Wiedmann return QETH_CAST_BROADCAST;
1623910a0a8fSJulian Wiedmann return QETH_CAST_UNICAST;
1624910a0a8fSJulian Wiedmann }
1625910a0a8fSJulian Wiedmann
qeth_l3_fill_header(struct qeth_qdio_out_q * queue,struct qeth_hdr * hdr,struct sk_buff * skb,__be16 proto,unsigned int data_len)1626b0abc4f5SJulian Wiedmann static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
1627b0abc4f5SJulian Wiedmann struct qeth_hdr *hdr, struct sk_buff *skb,
162817f3a8b5SJulian Wiedmann __be16 proto, unsigned int data_len)
16294a71df50SFrank Blaschka {
16305a541f6dSJulian Wiedmann struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3;
1631f13ade19SJulian Wiedmann struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
1632b0abc4f5SJulian Wiedmann struct qeth_card *card = queue->card;
16330cd6783dSJulian Wiedmann struct dst_entry *dst;
1634eca1d5c2SJulian Wiedmann int cast_type;
1635f13ade19SJulian Wiedmann
1636f6c13142SJulian Wiedmann hdr->hdr.l3.length = data_len;
16374a71df50SFrank Blaschka
1638e517b649SJulian Wiedmann if (skb_is_gso(skb)) {
16390aef8392SJulian Wiedmann hdr->hdr.l3.id = QETH_HEADER_TYPE_L3_TSO;
1640e517b649SJulian Wiedmann } else {
1641e517b649SJulian Wiedmann hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
16425a541f6dSJulian Wiedmann
1643e517b649SJulian Wiedmann if (skb->ip_summed == CHECKSUM_PARTIAL) {
164417f3a8b5SJulian Wiedmann qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, proto);
1645e517b649SJulian Wiedmann /* some HW requires combined L3+L4 csum offload: */
164617f3a8b5SJulian Wiedmann if (proto == htons(ETH_P_IP))
1647e517b649SJulian Wiedmann hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ;
1648e517b649SJulian Wiedmann }
1649e517b649SJulian Wiedmann }
1650e517b649SJulian Wiedmann
165117f3a8b5SJulian Wiedmann if (proto == htons(ETH_P_IP) || IS_IQD(card)) {
1652f13ade19SJulian Wiedmann /* NETIF_F_HW_VLAN_CTAG_TX */
1653df8a39deSJiri Pirko if (skb_vlan_tag_present(skb)) {
1654f13ade19SJulian Wiedmann hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_VLAN_FRAME;
1655df8a39deSJiri Pirko hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb);
16564a71df50SFrank Blaschka }
1657f13ade19SJulian Wiedmann } else if (veth->h_vlan_proto == htons(ETH_P_8021Q)) {
1658f13ade19SJulian Wiedmann hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_INCLUDE_VLAN_TAG;
1659f13ade19SJulian Wiedmann hdr->hdr.l3.vlan_id = ntohs(veth->h_vlan_TCI);
1660f13ade19SJulian Wiedmann }
16614a71df50SFrank Blaschka
16621d36cb47SFrank Blaschka rcu_read_lock();
166317f3a8b5SJulian Wiedmann dst = qeth_dst_check_rcu(skb, proto);
16640cd6783dSJulian Wiedmann
1665eca1d5c2SJulian Wiedmann if (IS_IQD(card) && skb_get_queue_mapping(skb) != QETH_IQD_MCAST_TXQ)
1666eca1d5c2SJulian Wiedmann cast_type = RTN_UNICAST;
1667eca1d5c2SJulian Wiedmann else
166817f3a8b5SJulian Wiedmann cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto);
1669eca1d5c2SJulian Wiedmann l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type);
1670eca1d5c2SJulian Wiedmann
1671a667fee1SJulian Wiedmann switch (proto) {
1672a667fee1SJulian Wiedmann case htons(ETH_P_IP):
16731d38c2e4SJulian Wiedmann l3_hdr->next_hop.addr.s6_addr32[3] =
16741d38c2e4SJulian Wiedmann qeth_next_hop_v4_rcu(skb, dst);
1675a667fee1SJulian Wiedmann break;
1676a667fee1SJulian Wiedmann case htons(ETH_P_IPV6):
16771d38c2e4SJulian Wiedmann l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst);
167887e7597bSDavid Miller
1679910a0a8fSJulian Wiedmann hdr->hdr.l3.flags |= QETH_HDR_IPV6;
1680379ac99eSJulian Wiedmann if (!IS_IQD(card))
1681910a0a8fSJulian Wiedmann hdr->hdr.l3.flags |= QETH_HDR_PASSTHRU;
1682a667fee1SJulian Wiedmann break;
1683a667fee1SJulian Wiedmann case htons(ETH_P_AF_IUCV):
1684a667fee1SJulian Wiedmann l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80);
1685a667fee1SJulian Wiedmann memcpy(&l3_hdr->next_hop.addr.s6_addr32[2],
1686a667fee1SJulian Wiedmann iucv_trans_hdr(skb)->destUserID, 8);
1687a667fee1SJulian Wiedmann l3_hdr->flags |= QETH_HDR_IPV6;
1688a667fee1SJulian Wiedmann break;
1689a667fee1SJulian Wiedmann default:
1690eca1d5c2SJulian Wiedmann /* OSA only: */
1691eca1d5c2SJulian Wiedmann l3_hdr->flags |= QETH_HDR_PASSTHRU;
16924a71df50SFrank Blaschka }
16931d36cb47SFrank Blaschka rcu_read_unlock();
16944a71df50SFrank Blaschka }
16954a71df50SFrank Blaschka
qeth_l3_fixup_headers(struct sk_buff * skb)16962d3986d1SJulian Wiedmann static void qeth_l3_fixup_headers(struct sk_buff *skb)
16972d3986d1SJulian Wiedmann {
16982d3986d1SJulian Wiedmann struct iphdr *iph = ip_hdr(skb);
16992d3986d1SJulian Wiedmann
17002d3986d1SJulian Wiedmann /* this is safe, IPv6 traffic takes a different path */
17012d3986d1SJulian Wiedmann if (skb->ip_summed == CHECKSUM_PARTIAL)
17022d3986d1SJulian Wiedmann iph->check = 0;
1703e517b649SJulian Wiedmann if (skb_is_gso(skb)) {
1704e517b649SJulian Wiedmann iph->tot_len = 0;
1705e517b649SJulian Wiedmann tcp_hdr(skb)->check = ~tcp_v4_check(0, iph->saddr,
1706e517b649SJulian Wiedmann iph->daddr, 0);
1707e517b649SJulian Wiedmann }
17082d3986d1SJulian Wiedmann }
17092d3986d1SJulian Wiedmann
qeth_l3_xmit(struct qeth_card * card,struct sk_buff * skb,struct qeth_qdio_out_q * queue,__be16 proto)1710356156b6SJulian Wiedmann static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
171117f3a8b5SJulian Wiedmann struct qeth_qdio_out_q *queue, __be16 proto)
1712a647a025SJulian Wiedmann {
171381ec5439SJulian Wiedmann unsigned int hw_hdr_len;
171481ec5439SJulian Wiedmann int rc;
1715e517b649SJulian Wiedmann
1716a647a025SJulian Wiedmann /* re-use the L2 header area for the HW header: */
171781ec5439SJulian Wiedmann hw_hdr_len = skb_is_gso(skb) ? sizeof(struct qeth_hdr_tso) :
171881ec5439SJulian Wiedmann sizeof(struct qeth_hdr);
1719a647a025SJulian Wiedmann rc = skb_cow_head(skb, hw_hdr_len - ETH_HLEN);
1720a647a025SJulian Wiedmann if (rc)
1721a647a025SJulian Wiedmann return rc;
1722a647a025SJulian Wiedmann skb_pull(skb, ETH_HLEN);
1723a647a025SJulian Wiedmann
17242d3986d1SJulian Wiedmann qeth_l3_fixup_headers(skb);
172517f3a8b5SJulian Wiedmann return qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header);
1726a647a025SJulian Wiedmann }
1727a647a025SJulian Wiedmann
qeth_l3_hard_start_xmit(struct sk_buff * skb,struct net_device * dev)1728ea1d4a0cSJulian Wiedmann static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
1729ea1d4a0cSJulian Wiedmann struct net_device *dev)
1730ea1d4a0cSJulian Wiedmann {
1731ea1d4a0cSJulian Wiedmann struct qeth_card *card = dev->ml_priv;
173217f3a8b5SJulian Wiedmann __be16 proto = vlan_get_protocol(skb);
17333a18d754SJulian Wiedmann u16 txq = skb_get_queue_mapping(skb);
1734ea1d4a0cSJulian Wiedmann struct qeth_qdio_out_q *queue;
1735eca1d5c2SJulian Wiedmann int rc;
1736b0abc4f5SJulian Wiedmann
1737eeac0e20SJulian Wiedmann if (!skb_is_gso(skb))
1738eeac0e20SJulian Wiedmann qdisc_skb_cb(skb)->pkt_len = skb->len;
1739ea1d4a0cSJulian Wiedmann if (IS_IQD(card)) {
17403a18d754SJulian Wiedmann queue = card->qdio.out_qs[qeth_iqd_translate_txq(dev, txq)];
17413a18d754SJulian Wiedmann
1742ea1d4a0cSJulian Wiedmann if (card->options.sniffer)
1743ea1d4a0cSJulian Wiedmann goto tx_drop;
174417f3a8b5SJulian Wiedmann
174517f3a8b5SJulian Wiedmann switch (proto) {
174617f3a8b5SJulian Wiedmann case htons(ETH_P_AF_IUCV):
174717f3a8b5SJulian Wiedmann if (card->options.cq != QETH_CQ_ENABLED)
17484a71df50SFrank Blaschka goto tx_drop;
174917f3a8b5SJulian Wiedmann break;
175017f3a8b5SJulian Wiedmann case htons(ETH_P_IP):
175117f3a8b5SJulian Wiedmann case htons(ETH_P_IPV6):
175217f3a8b5SJulian Wiedmann if (card->options.cq == QETH_CQ_ENABLED)
175317f3a8b5SJulian Wiedmann goto tx_drop;
175417f3a8b5SJulian Wiedmann break;
175517f3a8b5SJulian Wiedmann default:
175617f3a8b5SJulian Wiedmann goto tx_drop;
175717f3a8b5SJulian Wiedmann }
17583a18d754SJulian Wiedmann } else {
175973dc2dafSJulian Wiedmann queue = card->qdio.out_qs[txq];
17604a71df50SFrank Blaschka }
17614a71df50SFrank Blaschka
1762eca1d5c2SJulian Wiedmann if (!(dev->flags & IFF_BROADCAST) &&
1763c61dff3cSJulian Wiedmann qeth_l3_get_cast_type(skb, proto) == RTN_BROADCAST)
1764ea1d4a0cSJulian Wiedmann goto tx_drop;
1765ea1d4a0cSJulian Wiedmann
176617f3a8b5SJulian Wiedmann if (proto == htons(ETH_P_IP) || IS_IQD(card))
176717f3a8b5SJulian Wiedmann rc = qeth_l3_xmit(card, skb, queue, proto);
1768f13ade19SJulian Wiedmann else
176917f3a8b5SJulian Wiedmann rc = qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header);
1770ea1d4a0cSJulian Wiedmann
1771eeac0e20SJulian Wiedmann if (!rc)
1772ea1d4a0cSJulian Wiedmann return NETDEV_TX_OK;
17734a71df50SFrank Blaschka
17744a71df50SFrank Blaschka tx_drop:
1775b0abc4f5SJulian Wiedmann QETH_TXQ_STAT_INC(queue, tx_dropped);
1776104b4859SJulian Wiedmann kfree_skb(skb);
17774a71df50SFrank Blaschka return NETDEV_TX_OK;
17784a71df50SFrank Blaschka }
17794a71df50SFrank Blaschka
qeth_l3_set_rx_mode(struct net_device * dev)1780d0c74825SJulian Wiedmann static void qeth_l3_set_rx_mode(struct net_device *dev)
1781d0c74825SJulian Wiedmann {
1782d0c74825SJulian Wiedmann struct qeth_card *card = dev->ml_priv;
1783d0c74825SJulian Wiedmann
1784d0c74825SJulian Wiedmann schedule_work(&card->rx_mode_work);
1785d0c74825SJulian Wiedmann }
1786d0c74825SJulian Wiedmann
17874a71df50SFrank Blaschka /*
17884a71df50SFrank Blaschka * we need NOARP for IPv4 but we want neighbor solicitation for IPv6. Setting
17894a71df50SFrank Blaschka * NOARP on the netdevice is no option because it also turns off neighbor
17904a71df50SFrank Blaschka * solicitation. For IPv4 we install a neighbor_setup function. We don't want
17914a71df50SFrank Blaschka * arp resolution but we want the hard header (packet socket will work
17924a71df50SFrank Blaschka * e.g. tcpdump)
17934a71df50SFrank Blaschka */
qeth_l3_neigh_setup_noarp(struct neighbour * n)17944a71df50SFrank Blaschka static int qeth_l3_neigh_setup_noarp(struct neighbour *n)
17954a71df50SFrank Blaschka {
17964a71df50SFrank Blaschka n->nud_state = NUD_NOARP;
17974a71df50SFrank Blaschka memcpy(n->ha, "FAKELL", 6);
17984a71df50SFrank Blaschka n->output = n->ops->connected_output;
17994a71df50SFrank Blaschka return 0;
18004a71df50SFrank Blaschka }
18014a71df50SFrank Blaschka
18024a71df50SFrank Blaschka static int
qeth_l3_neigh_setup(struct net_device * dev,struct neigh_parms * np)18034a71df50SFrank Blaschka qeth_l3_neigh_setup(struct net_device *dev, struct neigh_parms *np)
18044a71df50SFrank Blaschka {
18054a71df50SFrank Blaschka if (np->tbl->family == AF_INET)
18064a71df50SFrank Blaschka np->neigh_setup = qeth_l3_neigh_setup_noarp;
18074a71df50SFrank Blaschka
18084a71df50SFrank Blaschka return 0;
18094a71df50SFrank Blaschka }
18104a71df50SFrank Blaschka
qeth_l3_osa_features_check(struct sk_buff * skb,struct net_device * dev,netdev_features_t features)1811f13ade19SJulian Wiedmann static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
1812f13ade19SJulian Wiedmann struct net_device *dev,
1813f13ade19SJulian Wiedmann netdev_features_t features)
1814f13ade19SJulian Wiedmann {
1815f9c48453SJulian Wiedmann if (vlan_get_protocol(skb) != htons(ETH_P_IP))
1816f13ade19SJulian Wiedmann features &= ~NETIF_F_HW_VLAN_CTAG_TX;
1817f13ade19SJulian Wiedmann return qeth_features_check(skb, dev, features);
1818f13ade19SJulian Wiedmann }
1819f13ade19SJulian Wiedmann
qeth_l3_iqd_select_queue(struct net_device * dev,struct sk_buff * skb,struct net_device * sb_dev)18203a18d754SJulian Wiedmann static u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
18213a18d754SJulian Wiedmann struct net_device *sb_dev)
18223a18d754SJulian Wiedmann {
1823c61dff3cSJulian Wiedmann __be16 proto = vlan_get_protocol(skb);
1824c61dff3cSJulian Wiedmann
1825c61dff3cSJulian Wiedmann return qeth_iqd_select_queue(dev, skb,
1826c61dff3cSJulian Wiedmann qeth_l3_get_cast_type(skb, proto), sb_dev);
18273a18d754SJulian Wiedmann }
18283a18d754SJulian Wiedmann
18293d58cefdSFrank Blaschka static const struct net_device_ops qeth_l3_netdev_ops = {
1830e22355eaSJulian Wiedmann .ndo_open = qeth_open,
1831e22355eaSJulian Wiedmann .ndo_stop = qeth_stop,
1832b0abc4f5SJulian Wiedmann .ndo_get_stats64 = qeth_get_stats64,
18338403b13cSFrank Blaschka .ndo_start_xmit = qeth_l3_hard_start_xmit,
18343a18d754SJulian Wiedmann .ndo_select_queue = qeth_l3_iqd_select_queue,
18358403b13cSFrank Blaschka .ndo_validate_addr = eth_validate_addr,
183600c163f1SJulian Wiedmann .ndo_set_rx_mode = qeth_l3_set_rx_mode,
1837a7605370SArnd Bergmann .ndo_eth_ioctl = qeth_do_ioctl,
18382decb0b7SJulian Wiedmann .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate,
18398f43fb00SThomas Richter .ndo_fix_features = qeth_fix_features,
18408f43fb00SThomas Richter .ndo_set_features = qeth_set_features,
18418403b13cSFrank Blaschka .ndo_tx_timeout = qeth_tx_timeout,
18428403b13cSFrank Blaschka };
18438403b13cSFrank Blaschka
18443d58cefdSFrank Blaschka static const struct net_device_ops qeth_l3_osa_netdev_ops = {
1845e22355eaSJulian Wiedmann .ndo_open = qeth_open,
1846e22355eaSJulian Wiedmann .ndo_stop = qeth_stop,
1847b0abc4f5SJulian Wiedmann .ndo_get_stats64 = qeth_get_stats64,
18483d58cefdSFrank Blaschka .ndo_start_xmit = qeth_l3_hard_start_xmit,
1849f13ade19SJulian Wiedmann .ndo_features_check = qeth_l3_osa_features_check,
18501b9e410fSJulian Wiedmann .ndo_select_queue = qeth_osa_select_queue,
18513d58cefdSFrank Blaschka .ndo_validate_addr = eth_validate_addr,
185200c163f1SJulian Wiedmann .ndo_set_rx_mode = qeth_l3_set_rx_mode,
1853a7605370SArnd Bergmann .ndo_eth_ioctl = qeth_do_ioctl,
18542decb0b7SJulian Wiedmann .ndo_siocdevprivate = qeth_l3_ndo_siocdevprivate,
18558f43fb00SThomas Richter .ndo_fix_features = qeth_fix_features,
18568f43fb00SThomas Richter .ndo_set_features = qeth_set_features,
18573d58cefdSFrank Blaschka .ndo_tx_timeout = qeth_tx_timeout,
18583d58cefdSFrank Blaschka .ndo_neigh_setup = qeth_l3_neigh_setup,
18593d58cefdSFrank Blaschka };
18603d58cefdSFrank Blaschka
qeth_l3_setup_netdev(struct qeth_card * card)1861cd652be5SJulian Wiedmann static int qeth_l3_setup_netdev(struct qeth_card *card)
18624a71df50SFrank Blaschka {
18638ec1e247SJulian Wiedmann struct net_device *dev = card->dev;
186482bf5c08SJulian Wiedmann unsigned int headroom;
18651aec42bcSThomas Richter int rc;
18661aec42bcSThomas Richter
1867379ac99eSJulian Wiedmann if (IS_OSD(card) || IS_OSX(card)) {
18683d58cefdSFrank Blaschka card->dev->netdev_ops = &qeth_l3_osa_netdev_ops;
18694a71df50SFrank Blaschka
18704a71df50SFrank Blaschka /*IPv6 address autoconfiguration stuff*/
18718ec1e247SJulian Wiedmann dev->dev_id = qeth_l3_get_unique_id(card, dev->dev_id);
18720f342945SJulian Wiedmann
1873379ac99eSJulian Wiedmann if (!IS_VM_NIC(card)) {
18746d69b1f1SJulian Wiedmann card->dev->features |= NETIF_F_SG;
18750f342945SJulian Wiedmann card->dev->hw_features |= NETIF_F_TSO |
18760f342945SJulian Wiedmann NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
18770f342945SJulian Wiedmann card->dev->vlan_features |= NETIF_F_TSO |
18780f342945SJulian Wiedmann NETIF_F_RXCSUM | NETIF_F_IP_CSUM;
1879c5e631a8SFrank Blaschka }
1880571f9dd8SKittipon Meesompop
1881571f9dd8SKittipon Meesompop if (qeth_is_supported6(card, IPA_OUTBOUND_CHECKSUM_V6)) {
1882571f9dd8SKittipon Meesompop card->dev->hw_features |= NETIF_F_IPV6_CSUM;
1883571f9dd8SKittipon Meesompop card->dev->vlan_features |= NETIF_F_IPV6_CSUM;
1884571f9dd8SKittipon Meesompop }
188582bf5c08SJulian Wiedmann if (qeth_is_supported6(card, IPA_OUTBOUND_TSO)) {
188682bf5c08SJulian Wiedmann card->dev->hw_features |= NETIF_F_TSO6;
188782bf5c08SJulian Wiedmann card->dev->vlan_features |= NETIF_F_TSO6;
188882bf5c08SJulian Wiedmann }
188982bf5c08SJulian Wiedmann
189082bf5c08SJulian Wiedmann /* allow for de-acceleration of NETIF_F_HW_VLAN_CTAG_TX: */
189182bf5c08SJulian Wiedmann if (card->dev->hw_features & NETIF_F_TSO6)
189282bf5c08SJulian Wiedmann headroom = sizeof(struct qeth_hdr_tso) + VLAN_HLEN;
189382bf5c08SJulian Wiedmann else if (card->dev->hw_features & NETIF_F_TSO)
189482bf5c08SJulian Wiedmann headroom = sizeof(struct qeth_hdr_tso);
189582bf5c08SJulian Wiedmann else
189682bf5c08SJulian Wiedmann headroom = sizeof(struct qeth_hdr) + VLAN_HLEN;
1897379ac99eSJulian Wiedmann } else if (IS_IQD(card)) {
18984a71df50SFrank Blaschka card->dev->flags |= IFF_NOARP;
18993d58cefdSFrank Blaschka card->dev->netdev_ops = &qeth_l3_netdev_ops;
190082bf5c08SJulian Wiedmann headroom = sizeof(struct qeth_hdr) - ETH_HLEN;
1901a647a025SJulian Wiedmann
19021aec42bcSThomas Richter rc = qeth_l3_iqd_read_initial_mac(card);
19031aec42bcSThomas Richter if (rc)
1904cd652be5SJulian Wiedmann return rc;
19054a71df50SFrank Blaschka } else
19064a71df50SFrank Blaschka return -ENODEV;
19074a71df50SFrank Blaschka
190882bf5c08SJulian Wiedmann card->dev->needed_headroom = headroom;
1909f646968fSPatrick McHardy card->dev->features |= NETIF_F_HW_VLAN_CTAG_TX |
1910d96a8c69SJulian Wiedmann NETIF_F_HW_VLAN_CTAG_RX;
1911a647a025SJulian Wiedmann
191202875878SEric Dumazet netif_keep_dst(card->dev);
191382bf5c08SJulian Wiedmann if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6))
1914ee8b7a11SJakub Kicinski netif_set_tso_max_size(card->dev,
1915371a1e7aSJulian Wiedmann PAGE_SIZE * (QETH_MAX_BUFFER_ELEMENTS(card) - 1));
19164a71df50SFrank Blaschka
1917b48b89f9SJakub Kicinski netif_napi_add(card->dev, &card->napi, qeth_poll);
1918cd652be5SJulian Wiedmann return register_netdev(card->dev);
19194a71df50SFrank Blaschka }
19204a71df50SFrank Blaschka
192179a04e40SUrsula Braun static const struct device_type qeth_l3_devtype = {
192279a04e40SUrsula Braun .name = "qeth_layer3",
192379a04e40SUrsula Braun .groups = qeth_l3_attr_groups,
192479a04e40SUrsula Braun };
192579a04e40SUrsula Braun
qeth_l3_probe_device(struct ccwgroup_device * gdev)19264a71df50SFrank Blaschka static int qeth_l3_probe_device(struct ccwgroup_device *gdev)
19274a71df50SFrank Blaschka {
19284a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&gdev->dev);
19299111e788SUrsula Braun int rc;
19304a71df50SFrank Blaschka
19317221b727SJulian Wiedmann hash_init(card->ip_htable);
1932df2a2a52SJulian Wiedmann mutex_init(&card->ip_lock);
19337686e4b6SJulian Wiedmann card->cmd_wq = alloc_ordered_workqueue("%s_cmd", 0,
19347686e4b6SJulian Wiedmann dev_name(&gdev->dev));
19357686e4b6SJulian Wiedmann if (!card->cmd_wq)
19367686e4b6SJulian Wiedmann return -ENOMEM;
19377221b727SJulian Wiedmann
1938ae57ea7aSJulian Wiedmann if (gdev->dev.type) {
19390b8da811SJulian Wiedmann rc = device_add_groups(&gdev->dev, qeth_l3_attr_groups);
19407686e4b6SJulian Wiedmann if (rc) {
19417686e4b6SJulian Wiedmann destroy_workqueue(card->cmd_wq);
19429111e788SUrsula Braun return rc;
194379a04e40SUrsula Braun }
1944ae57ea7aSJulian Wiedmann } else {
1945ae57ea7aSJulian Wiedmann gdev->dev.type = &qeth_l3_devtype;
19467686e4b6SJulian Wiedmann }
19477221b727SJulian Wiedmann
1948d0c74825SJulian Wiedmann INIT_WORK(&card->rx_mode_work, qeth_l3_rx_mode_work);
19494a71df50SFrank Blaschka return 0;
19504a71df50SFrank Blaschka }
19514a71df50SFrank Blaschka
qeth_l3_remove_device(struct ccwgroup_device * cgdev)19524a71df50SFrank Blaschka static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
19534a71df50SFrank Blaschka {
19544a71df50SFrank Blaschka struct qeth_card *card = dev_get_drvdata(&cgdev->dev);
19554a71df50SFrank Blaschka
1956ae57ea7aSJulian Wiedmann if (cgdev->dev.type != &qeth_l3_devtype)
19570b8da811SJulian Wiedmann device_remove_groups(&cgdev->dev, qeth_l3_attr_groups);
19589dc48cccSUrsula Braun
1959f2148565SUrsula Braun qeth_set_allowed_threads(card, 0, 1);
19604a71df50SFrank Blaschka wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
19614a71df50SFrank Blaschka
1962b41b554cSJulian Wiedmann if (cgdev->state == CCWGROUP_ONLINE)
19630b9902c1SJulian Wiedmann qeth_set_offline(card, card->discipline, false);
19644a71df50SFrank Blaschka
1965cd652be5SJulian Wiedmann if (card->dev->reg_state == NETREG_REGISTERED)
19664a71df50SFrank Blaschka unregister_netdev(card->dev);
19677686e4b6SJulian Wiedmann
19687686e4b6SJulian Wiedmann destroy_workqueue(card->cmd_wq);
19695f78e29cSLakhvich Dmitriy qeth_l3_clear_ip_htable(card, 0);
19704a71df50SFrank Blaschka qeth_l3_clear_ipato_list(card);
19714a71df50SFrank Blaschka }
19724a71df50SFrank Blaschka
qeth_l3_set_online(struct qeth_card * card,bool carrier_ok)1973b7ea041bSJulian Wiedmann static int qeth_l3_set_online(struct qeth_card *card, bool carrier_ok)
19744a71df50SFrank Blaschka {
1975fa3d2e60SJulian Wiedmann struct net_device *dev = card->dev;
19764a71df50SFrank Blaschka int rc = 0;
19774a71df50SFrank Blaschka
19784a71df50SFrank Blaschka /* softsetup */
197957a688aaSJulian Wiedmann QETH_CARD_TEXT(card, 2, "softsetp");
19804a71df50SFrank Blaschka
19814a71df50SFrank Blaschka rc = qeth_l3_setadapter_parms(card);
19824a71df50SFrank Blaschka if (rc)
198357a688aaSJulian Wiedmann QETH_CARD_TEXT_(card, 2, "2err%04x", rc);
198476b11f8eSUrsula Braun if (!card->options.sniffer) {
198517d9723eSJulian Wiedmann qeth_l3_start_ipassists(card);
198617d9723eSJulian Wiedmann
19874a71df50SFrank Blaschka rc = qeth_l3_setrouting_v4(card);
19884a71df50SFrank Blaschka if (rc)
198957a688aaSJulian Wiedmann QETH_CARD_TEXT_(card, 2, "4err%04x", rc);
19904a71df50SFrank Blaschka rc = qeth_l3_setrouting_v6(card);
19914a71df50SFrank Blaschka if (rc)
199257a688aaSJulian Wiedmann QETH_CARD_TEXT_(card, 2, "5err%04x", rc);
199376b11f8eSUrsula Braun }
19944a71df50SFrank Blaschka
19954a71df50SFrank Blaschka card->state = CARD_STATE_SOFTSETUP;
19964a71df50SFrank Blaschka
19974a71df50SFrank Blaschka qeth_set_allowed_threads(card, 0xffffffff, 0);
19985f78e29cSLakhvich Dmitriy qeth_l3_recover_ip(card);
1999d025da9eSJulian Wiedmann
2000cd652be5SJulian Wiedmann if (dev->reg_state != NETREG_REGISTERED) {
2001cd652be5SJulian Wiedmann rc = qeth_l3_setup_netdev(card);
2002fa3d2e60SJulian Wiedmann if (rc)
2003f2bcf9ddSJulian Wiedmann goto err_setup;
2004cd652be5SJulian Wiedmann
2005cd652be5SJulian Wiedmann if (carrier_ok)
2006cd652be5SJulian Wiedmann netif_carrier_on(dev);
20079aa17df3SJulian Wiedmann } else {
2008fa3d2e60SJulian Wiedmann rtnl_lock();
2009bb5ab541SJulian Wiedmann rc = qeth_set_real_num_tx_queues(card,
2010bb5ab541SJulian Wiedmann qeth_tx_actual_queues(card));
2011bb5ab541SJulian Wiedmann if (rc) {
2012bb5ab541SJulian Wiedmann rtnl_unlock();
2013bb5ab541SJulian Wiedmann goto err_set_queues;
2014bb5ab541SJulian Wiedmann }
2015bb5ab541SJulian Wiedmann
2016fa3d2e60SJulian Wiedmann if (carrier_ok)
2017fa3d2e60SJulian Wiedmann netif_carrier_on(dev);
2018fa3d2e60SJulian Wiedmann else
2019fa3d2e60SJulian Wiedmann netif_carrier_off(dev);
2020fa3d2e60SJulian Wiedmann
2021e6e771b3SJulian Wiedmann netif_device_attach(dev);
2022fa3d2e60SJulian Wiedmann qeth_enable_hw_features(dev);
2023fa3d2e60SJulian Wiedmann
20241cfef80dSAlexandra Winter if (netif_running(dev)) {
20251cfef80dSAlexandra Winter local_bh_disable();
20261cfef80dSAlexandra Winter napi_schedule(&card->napi);
20271cfef80dSAlexandra Winter /* kick-start the NAPI softirq: */
20281cfef80dSAlexandra Winter local_bh_enable();
2029fa3d2e60SJulian Wiedmann }
20304763b0a0SUrsula Braun rtnl_unlock();
20314a71df50SFrank Blaschka }
20324a71df50SFrank Blaschka return 0;
2033b7ea041bSJulian Wiedmann
2034bb5ab541SJulian Wiedmann err_set_queues:
2035f2bcf9ddSJulian Wiedmann err_setup:
2036f2bcf9ddSJulian Wiedmann qeth_set_allowed_threads(card, 0, 1);
2037f2bcf9ddSJulian Wiedmann card->state = CARD_STATE_DOWN;
2038f2bcf9ddSJulian Wiedmann qeth_l3_clear_ip_htable(card, 1);
2039aa909224SUrsula Braun return rc;
20404a71df50SFrank Blaschka }
20414a71df50SFrank Blaschka
qeth_l3_set_offline(struct qeth_card * card)204291003f35SJulian Wiedmann static void qeth_l3_set_offline(struct qeth_card *card)
20434a71df50SFrank Blaschka {
2044f2bcf9ddSJulian Wiedmann qeth_set_allowed_threads(card, 0, 1);
2045f2bcf9ddSJulian Wiedmann qeth_l3_drain_rx_mode_cache(card);
2046f2bcf9ddSJulian Wiedmann
2047f2bcf9ddSJulian Wiedmann if (card->options.sniffer &&
2048f2bcf9ddSJulian Wiedmann (card->info.promisc_mode == SET_PROMISC_MODE_ON))
2049f2bcf9ddSJulian Wiedmann qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
2050f2bcf9ddSJulian Wiedmann
2051f2bcf9ddSJulian Wiedmann if (card->state == CARD_STATE_SOFTSETUP) {
2052f2bcf9ddSJulian Wiedmann card->state = CARD_STATE_DOWN;
2053f2bcf9ddSJulian Wiedmann qeth_l3_clear_ip_htable(card, 1);
2054f2bcf9ddSJulian Wiedmann }
20554a71df50SFrank Blaschka }
20564a71df50SFrank Blaschka
2057c044dc21SEugene Crosser /* Returns zero if the command is successfully "consumed" */
qeth_l3_control_event(struct qeth_card * card,struct qeth_ipa_cmd * cmd)2058c044dc21SEugene Crosser static int qeth_l3_control_event(struct qeth_card *card,
2059c044dc21SEugene Crosser struct qeth_ipa_cmd *cmd)
2060c044dc21SEugene Crosser {
2061c044dc21SEugene Crosser return 1;
2062c044dc21SEugene Crosser }
2063c044dc21SEugene Crosser
206450144f67SJulian Wiedmann const struct qeth_discipline qeth_l3_discipline = {
2065c041f2d4SSebastian Ott .setup = qeth_l3_probe_device,
20664a71df50SFrank Blaschka .remove = qeth_l3_remove_device,
20674a71df50SFrank Blaschka .set_online = qeth_l3_set_online,
20684a71df50SFrank Blaschka .set_offline = qeth_l3_set_offline,
2069c044dc21SEugene Crosser .control_event_handler = qeth_l3_control_event,
20704a71df50SFrank Blaschka };
2071c041f2d4SSebastian Ott EXPORT_SYMBOL_GPL(qeth_l3_discipline);
20724a71df50SFrank Blaschka
qeth_l3_handle_ip_event(struct qeth_card * card,struct qeth_ipaddr * addr,unsigned long event)2073d66b1c0dSJulian Wiedmann static int qeth_l3_handle_ip_event(struct qeth_card *card,
2074d66b1c0dSJulian Wiedmann struct qeth_ipaddr *addr,
2075d66b1c0dSJulian Wiedmann unsigned long event)
2076d66b1c0dSJulian Wiedmann {
2077d66b1c0dSJulian Wiedmann switch (event) {
2078d66b1c0dSJulian Wiedmann case NETDEV_UP:
207905a17851SJulian Wiedmann qeth_l3_modify_ip(card, addr, true);
2080d66b1c0dSJulian Wiedmann return NOTIFY_OK;
2081d66b1c0dSJulian Wiedmann case NETDEV_DOWN:
208205a17851SJulian Wiedmann qeth_l3_modify_ip(card, addr, false);
2083d66b1c0dSJulian Wiedmann return NOTIFY_OK;
2084d66b1c0dSJulian Wiedmann default:
2085d66b1c0dSJulian Wiedmann return NOTIFY_DONE;
2086d66b1c0dSJulian Wiedmann }
2087d66b1c0dSJulian Wiedmann }
2088d66b1c0dSJulian Wiedmann
20897686e4b6SJulian Wiedmann struct qeth_l3_ip_event_work {
20907686e4b6SJulian Wiedmann struct work_struct work;
20917686e4b6SJulian Wiedmann struct qeth_card *card;
20927686e4b6SJulian Wiedmann struct qeth_ipaddr addr;
20937686e4b6SJulian Wiedmann };
20947686e4b6SJulian Wiedmann
20957686e4b6SJulian Wiedmann #define to_ip_work(w) container_of((w), struct qeth_l3_ip_event_work, work)
20967686e4b6SJulian Wiedmann
qeth_l3_add_ip_worker(struct work_struct * work)20977686e4b6SJulian Wiedmann static void qeth_l3_add_ip_worker(struct work_struct *work)
20987686e4b6SJulian Wiedmann {
20997686e4b6SJulian Wiedmann struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
21007686e4b6SJulian Wiedmann
21017686e4b6SJulian Wiedmann qeth_l3_modify_ip(ip_work->card, &ip_work->addr, true);
21027686e4b6SJulian Wiedmann kfree(work);
21037686e4b6SJulian Wiedmann }
21047686e4b6SJulian Wiedmann
qeth_l3_delete_ip_worker(struct work_struct * work)21057686e4b6SJulian Wiedmann static void qeth_l3_delete_ip_worker(struct work_struct *work)
21067686e4b6SJulian Wiedmann {
21077686e4b6SJulian Wiedmann struct qeth_l3_ip_event_work *ip_work = to_ip_work(work);
21087686e4b6SJulian Wiedmann
21097686e4b6SJulian Wiedmann qeth_l3_modify_ip(ip_work->card, &ip_work->addr, false);
21107686e4b6SJulian Wiedmann kfree(work);
21117686e4b6SJulian Wiedmann }
21127686e4b6SJulian Wiedmann
qeth_l3_get_card_from_dev(struct net_device * dev)2113b9caa98cSJulian Wiedmann static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
2114b9caa98cSJulian Wiedmann {
2115b9caa98cSJulian Wiedmann if (is_vlan_dev(dev))
2116b9caa98cSJulian Wiedmann dev = vlan_dev_real_dev(dev);
2117b9caa98cSJulian Wiedmann if (dev->netdev_ops == &qeth_l3_osa_netdev_ops ||
2118b9caa98cSJulian Wiedmann dev->netdev_ops == &qeth_l3_netdev_ops)
2119b9caa98cSJulian Wiedmann return (struct qeth_card *) dev->ml_priv;
2120b9caa98cSJulian Wiedmann return NULL;
2121b9caa98cSJulian Wiedmann }
2122b9caa98cSJulian Wiedmann
qeth_l3_ip_event(struct notifier_block * this,unsigned long event,void * ptr)21234a71df50SFrank Blaschka static int qeth_l3_ip_event(struct notifier_block *this,
21244a71df50SFrank Blaschka unsigned long event, void *ptr)
21254a71df50SFrank Blaschka {
21264a71df50SFrank Blaschka struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
2127b9caa98cSJulian Wiedmann struct net_device *dev = ifa->ifa_dev->dev;
2128d66b1c0dSJulian Wiedmann struct qeth_ipaddr addr;
21294a71df50SFrank Blaschka struct qeth_card *card;
21304a71df50SFrank Blaschka
21314a71df50SFrank Blaschka card = qeth_l3_get_card_from_dev(dev);
21324a71df50SFrank Blaschka if (!card)
21334a71df50SFrank Blaschka return NOTIFY_DONE;
21346d8823dbSfrank.blaschka@de.ibm.com QETH_CARD_TEXT(card, 3, "ipevent");
21354a71df50SFrank Blaschka
2136d66b1c0dSJulian Wiedmann qeth_l3_init_ipaddr(&addr, QETH_IP_TYPE_NORMAL, QETH_PROT_IPV4);
21371b40d4b2SJulian Wiedmann addr.u.a4.addr = ifa->ifa_address;
2138490df971SJulian Wiedmann addr.u.a4.mask = ifa->ifa_mask;
21394a71df50SFrank Blaschka
2140d66b1c0dSJulian Wiedmann return qeth_l3_handle_ip_event(card, &addr, event);
21414a71df50SFrank Blaschka }
21424a71df50SFrank Blaschka
21434a71df50SFrank Blaschka static struct notifier_block qeth_l3_ip_notifier = {
21444a71df50SFrank Blaschka qeth_l3_ip_event,
21454a71df50SFrank Blaschka NULL,
21464a71df50SFrank Blaschka };
21474a71df50SFrank Blaschka
qeth_l3_ip6_event(struct notifier_block * this,unsigned long event,void * ptr)21484a71df50SFrank Blaschka static int qeth_l3_ip6_event(struct notifier_block *this,
21494a71df50SFrank Blaschka unsigned long event, void *ptr)
21504a71df50SFrank Blaschka {
21514a71df50SFrank Blaschka struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
2152b9caa98cSJulian Wiedmann struct net_device *dev = ifa->idev->dev;
21537686e4b6SJulian Wiedmann struct qeth_l3_ip_event_work *ip_work;
21544a71df50SFrank Blaschka struct qeth_card *card;
21554a71df50SFrank Blaschka
21567686e4b6SJulian Wiedmann if (event != NETDEV_UP && event != NETDEV_DOWN)
21577686e4b6SJulian Wiedmann return NOTIFY_DONE;
21587686e4b6SJulian Wiedmann
21594a71df50SFrank Blaschka card = qeth_l3_get_card_from_dev(dev);
21604a71df50SFrank Blaschka if (!card)
21614a71df50SFrank Blaschka return NOTIFY_DONE;
2162847a50fdSCarsten Otte QETH_CARD_TEXT(card, 3, "ip6event");
21634a71df50SFrank Blaschka if (!qeth_is_supported(card, IPA_IPV6))
21644a71df50SFrank Blaschka return NOTIFY_DONE;
21654a71df50SFrank Blaschka
21667686e4b6SJulian Wiedmann ip_work = kmalloc(sizeof(*ip_work), GFP_ATOMIC);
21677686e4b6SJulian Wiedmann if (!ip_work)
21687686e4b6SJulian Wiedmann return NOTIFY_DONE;
21694a71df50SFrank Blaschka
21707686e4b6SJulian Wiedmann if (event == NETDEV_UP)
21717686e4b6SJulian Wiedmann INIT_WORK(&ip_work->work, qeth_l3_add_ip_worker);
21727686e4b6SJulian Wiedmann else
21737686e4b6SJulian Wiedmann INIT_WORK(&ip_work->work, qeth_l3_delete_ip_worker);
21747686e4b6SJulian Wiedmann
21757686e4b6SJulian Wiedmann ip_work->card = card;
21767686e4b6SJulian Wiedmann qeth_l3_init_ipaddr(&ip_work->addr, QETH_IP_TYPE_NORMAL,
21777686e4b6SJulian Wiedmann QETH_PROT_IPV6);
21787686e4b6SJulian Wiedmann ip_work->addr.u.a6.addr = ifa->addr;
21797686e4b6SJulian Wiedmann ip_work->addr.u.a6.pfxlen = ifa->prefix_len;
21807686e4b6SJulian Wiedmann
21817686e4b6SJulian Wiedmann queue_work(card->cmd_wq, &ip_work->work);
21827686e4b6SJulian Wiedmann return NOTIFY_OK;
21834a71df50SFrank Blaschka }
21844a71df50SFrank Blaschka
21854a71df50SFrank Blaschka static struct notifier_block qeth_l3_ip6_notifier = {
21864a71df50SFrank Blaschka qeth_l3_ip6_event,
21874a71df50SFrank Blaschka NULL,
21884a71df50SFrank Blaschka };
21894a71df50SFrank Blaschka
qeth_l3_register_notifiers(void)21904a71df50SFrank Blaschka static int qeth_l3_register_notifiers(void)
21914a71df50SFrank Blaschka {
21924a71df50SFrank Blaschka int rc;
21934a71df50SFrank Blaschka
2194847a50fdSCarsten Otte QETH_DBF_TEXT(SETUP, 5, "regnotif");
21954a71df50SFrank Blaschka rc = register_inetaddr_notifier(&qeth_l3_ip_notifier);
21964a71df50SFrank Blaschka if (rc)
21974a71df50SFrank Blaschka return rc;
21984a71df50SFrank Blaschka rc = register_inet6addr_notifier(&qeth_l3_ip6_notifier);
21994a71df50SFrank Blaschka if (rc) {
22004a71df50SFrank Blaschka unregister_inetaddr_notifier(&qeth_l3_ip_notifier);
22014a71df50SFrank Blaschka return rc;
22024a71df50SFrank Blaschka }
22034a71df50SFrank Blaschka return 0;
22044a71df50SFrank Blaschka }
22054a71df50SFrank Blaschka
qeth_l3_unregister_notifiers(void)22064a71df50SFrank Blaschka static void qeth_l3_unregister_notifiers(void)
22074a71df50SFrank Blaschka {
2208847a50fdSCarsten Otte QETH_DBF_TEXT(SETUP, 5, "unregnot");
220918af5c17SStefan Raspl WARN_ON(unregister_inetaddr_notifier(&qeth_l3_ip_notifier));
221018af5c17SStefan Raspl WARN_ON(unregister_inet6addr_notifier(&qeth_l3_ip6_notifier));
22114a71df50SFrank Blaschka }
22124a71df50SFrank Blaschka
qeth_l3_init(void)22134a71df50SFrank Blaschka static int __init qeth_l3_init(void)
22144a71df50SFrank Blaschka {
221574eacdb9SFrank Blaschka pr_info("register layer 3 discipline\n");
2216c062204fSJulian Wiedmann return qeth_l3_register_notifiers();
22174a71df50SFrank Blaschka }
22184a71df50SFrank Blaschka
qeth_l3_exit(void)22194a71df50SFrank Blaschka static void __exit qeth_l3_exit(void)
22204a71df50SFrank Blaschka {
22214a71df50SFrank Blaschka qeth_l3_unregister_notifiers();
222274eacdb9SFrank Blaschka pr_info("unregister layer 3 discipline\n");
22234a71df50SFrank Blaschka }
22244a71df50SFrank Blaschka
22254a71df50SFrank Blaschka module_init(qeth_l3_init);
22264a71df50SFrank Blaschka module_exit(qeth_l3_exit);
22274a71df50SFrank Blaschka MODULE_AUTHOR("Frank Blaschka <frank.blaschka@de.ibm.com>");
22284a71df50SFrank Blaschka MODULE_DESCRIPTION("qeth layer 3 discipline");
22294a71df50SFrank Blaschka MODULE_LICENSE("GPL");
2230