xref: /openbmc/linux/drivers/s390/net/qeth_l3_main.c (revision c595db6d7c8bcf87ef42204391fa890e5950e566)
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