Skip to content

Commit 058db71

Browse files
julianwiedmanngregkh
authored andcommitted
s390/qeth: fix IP address lookup for L3 devices
[ Upstream commit c5c48c5 ] Current code ("qeth_l3_ip_from_hash()") matches a queried address object against objects in the IP table by IP address, Mask/Prefix Length and MAC address ("qeth_l3_ipaddrs_is_equal()"). But what callers actually require is either a) "is this IP address registered" (ie. match by IP address only), before adding a new address. b) or "is this address object registered" (ie. match all relevant attributes), before deleting an address. Right now 1. the ADD path is too strict in its lookup, and eg. doesn't detect conflicts between an existing NORMAL address and a new VIPA address (because the NORMAL address will have mask != 0, while VIPA has a mask == 0), 2. the DELETE path is not strict enough, and eg. allows del_rxip() to delete a VIPA address as long as the IP address matches. Fix all this by adding helpers (_addr_match_ip() and _addr_match_all()) that do the appropriate checking. Note that the ADD path for NORMAL addresses is special, as qeth keeps track of how many times such an address is in use (and there is no immediate way of returning errors to the caller). So when a requested NORMAL address _fully_ matches an existing one, it's not considered a conflict and we merely increment the refcount. Fixes: 5f78e29 ("qeth: optimize IP handling in rx_mode callback") Signed-off-by: Julian Wiedmann <jwi@linux.vnet.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2a3db83 commit 058db71

2 files changed

Lines changed: 74 additions & 51 deletions

File tree

drivers/s390/net/qeth_l3.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,40 @@ struct qeth_ipaddr {
3939
unsigned int pfxlen;
4040
} a6;
4141
} u;
42-
4342
};
43+
44+
static inline bool qeth_l3_addr_match_ip(struct qeth_ipaddr *a1,
45+
struct qeth_ipaddr *a2)
46+
{
47+
if (a1->proto != a2->proto)
48+
return false;
49+
if (a1->proto == QETH_PROT_IPV6)
50+
return ipv6_addr_equal(&a1->u.a6.addr, &a2->u.a6.addr);
51+
return a1->u.a4.addr == a2->u.a4.addr;
52+
}
53+
54+
static inline bool qeth_l3_addr_match_all(struct qeth_ipaddr *a1,
55+
struct qeth_ipaddr *a2)
56+
{
57+
/* Assumes that the pair was obtained via qeth_l3_addr_find_by_ip(),
58+
* so 'proto' and 'addr' match for sure.
59+
*
60+
* For ucast:
61+
* - 'mac' is always 0.
62+
* - 'mask'/'pfxlen' for RXIP/VIPA is always 0. For NORMAL, matching
63+
* values are required to avoid mixups in takeover eligibility.
64+
*
65+
* For mcast,
66+
* - 'mac' is mapped from the IP, and thus always matches.
67+
* - 'mask'/'pfxlen' is always 0.
68+
*/
69+
if (a1->type != a2->type)
70+
return false;
71+
if (a1->proto == QETH_PROT_IPV6)
72+
return a1->u.a6.pfxlen == a2->u.a6.pfxlen;
73+
return a1->u.a4.mask == a2->u.a4.mask;
74+
}
75+
4476
static inline u64 qeth_l3_ipaddr_hash(struct qeth_ipaddr *addr)
4577
{
4678
u64 ret = 0;

drivers/s390/net/qeth_l3_main.c

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ int qeth_l3_string_to_ipaddr(const char *buf, enum qeth_prot_versions proto,
154154
return -EINVAL;
155155
}
156156

157+
static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
158+
struct qeth_ipaddr *query)
159+
{
160+
u64 key = qeth_l3_ipaddr_hash(query);
161+
struct qeth_ipaddr *addr;
162+
163+
if (query->is_multicast) {
164+
hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
165+
if (qeth_l3_addr_match_ip(addr, query))
166+
return addr;
167+
} else {
168+
hash_for_each_possible(card->ip_htable, addr, hnode, key)
169+
if (qeth_l3_addr_match_ip(addr, query))
170+
return addr;
171+
}
172+
return NULL;
173+
}
174+
157175
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
158176
{
159177
int i, j;
@@ -207,34 +225,6 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
207225
return rc;
208226
}
209227

210-
inline int
211-
qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
212-
{
213-
return addr1->proto == addr2->proto &&
214-
!memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
215-
!memcmp(&addr1->mac, &addr2->mac, sizeof(addr1->mac));
216-
}
217-
218-
static struct qeth_ipaddr *
219-
qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
220-
{
221-
struct qeth_ipaddr *addr;
222-
223-
if (tmp_addr->is_multicast) {
224-
hash_for_each_possible(card->ip_mc_htable, addr,
225-
hnode, qeth_l3_ipaddr_hash(tmp_addr))
226-
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
227-
return addr;
228-
} else {
229-
hash_for_each_possible(card->ip_htable, addr,
230-
hnode, qeth_l3_ipaddr_hash(tmp_addr))
231-
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
232-
return addr;
233-
}
234-
235-
return NULL;
236-
}
237-
238228
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
239229
{
240230
int rc = 0;
@@ -249,8 +239,8 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
249239
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
250240
}
251241

252-
addr = qeth_l3_ip_from_hash(card, tmp_addr);
253-
if (!addr)
242+
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
243+
if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
254244
return -ENOENT;
255245

256246
addr->ref_counter--;
@@ -272,6 +262,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
272262
{
273263
int rc = 0;
274264
struct qeth_ipaddr *addr;
265+
char buf[40];
275266

276267
QETH_CARD_TEXT(card, 4, "addip");
277268

@@ -282,8 +273,20 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
282273
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
283274
}
284275

285-
addr = qeth_l3_ip_from_hash(card, tmp_addr);
286-
if (!addr) {
276+
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
277+
if (addr) {
278+
if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
279+
return -EADDRINUSE;
280+
if (qeth_l3_addr_match_all(addr, tmp_addr)) {
281+
addr->ref_counter++;
282+
return 0;
283+
}
284+
qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
285+
buf);
286+
dev_warn(&card->gdev->dev,
287+
"Registering IP address %s failed\n", buf);
288+
return -EADDRINUSE;
289+
} else {
287290
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
288291
if (!addr)
289292
return -ENOMEM;
@@ -331,11 +334,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
331334
hash_del(&addr->hnode);
332335
kfree(addr);
333336
}
334-
} else {
335-
if (addr->type == QETH_IP_TYPE_NORMAL)
336-
addr->ref_counter++;
337337
}
338-
339338
return rc;
340339
}
341340

@@ -719,12 +718,7 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
719718
return -ENOMEM;
720719

721720
spin_lock_bh(&card->ip_lock);
722-
723-
if (qeth_l3_ip_from_hash(card, ipaddr))
724-
rc = -EEXIST;
725-
else
726-
qeth_l3_add_ip(card, ipaddr);
727-
721+
rc = qeth_l3_add_ip(card, ipaddr);
728722
spin_unlock_bh(&card->ip_lock);
729723

730724
kfree(ipaddr);
@@ -787,12 +781,7 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
787781
return -ENOMEM;
788782

789783
spin_lock_bh(&card->ip_lock);
790-
791-
if (qeth_l3_ip_from_hash(card, ipaddr))
792-
rc = -EEXIST;
793-
else
794-
qeth_l3_add_ip(card, ipaddr);
795-
784+
rc = qeth_l3_add_ip(card, ipaddr);
796785
spin_unlock_bh(&card->ip_lock);
797786

798787
kfree(ipaddr);
@@ -1437,8 +1426,9 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
14371426
memcpy(tmp->mac, buf, sizeof(tmp->mac));
14381427
tmp->is_multicast = 1;
14391428

1440-
ipm = qeth_l3_ip_from_hash(card, tmp);
1429+
ipm = qeth_l3_find_addr_by_ip(card, tmp);
14411430
if (ipm) {
1431+
/* for mcast, by-IP match means full match */
14421432
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
14431433
} else {
14441434
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
@@ -1521,8 +1511,9 @@ qeth_l3_add_mc6_to_hash(struct qeth_card *card, struct inet6_dev *in6_dev)
15211511
sizeof(struct in6_addr));
15221512
tmp->is_multicast = 1;
15231513

1524-
ipm = qeth_l3_ip_from_hash(card, tmp);
1514+
ipm = qeth_l3_find_addr_by_ip(card, tmp);
15251515
if (ipm) {
1516+
/* for mcast, by-IP match means full match */
15261517
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
15271518
continue;
15281519
}

0 commit comments

Comments
 (0)