Skip to content

Commit c5c48c5

Browse files
julianwiedmanndavem330
authored andcommitted
s390/qeth: fix IP address lookup for L3 devices
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4964c66 commit c5c48c5

File tree

2 files changed

+74
-51
lines changed

2 files changed

+74
-51
lines changed

drivers/s390/net/qeth_l3.h

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

drivers/s390/net/qeth_l3_main.c

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
6767
qeth_l3_ipaddr6_to_string(addr, buf);
6868
}
6969

70+
static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
71+
struct qeth_ipaddr *query)
72+
{
73+
u64 key = qeth_l3_ipaddr_hash(query);
74+
struct qeth_ipaddr *addr;
75+
76+
if (query->is_multicast) {
77+
hash_for_each_possible(card->ip_mc_htable, addr, hnode, key)
78+
if (qeth_l3_addr_match_ip(addr, query))
79+
return addr;
80+
} else {
81+
hash_for_each_possible(card->ip_htable, addr, hnode, key)
82+
if (qeth_l3_addr_match_ip(addr, query))
83+
return addr;
84+
}
85+
return NULL;
86+
}
87+
7088
static void qeth_l3_convert_addr_to_bits(u8 *addr, u8 *bits, int len)
7189
{
7290
int i, j;
@@ -120,34 +138,6 @@ static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
120138
return rc;
121139
}
122140

123-
inline int
124-
qeth_l3_ipaddrs_is_equal(struct qeth_ipaddr *addr1, struct qeth_ipaddr *addr2)
125-
{
126-
return addr1->proto == addr2->proto &&
127-
!memcmp(&addr1->u, &addr2->u, sizeof(addr1->u)) &&
128-
ether_addr_equal_64bits(addr1->mac, addr2->mac);
129-
}
130-
131-
static struct qeth_ipaddr *
132-
qeth_l3_ip_from_hash(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
133-
{
134-
struct qeth_ipaddr *addr;
135-
136-
if (tmp_addr->is_multicast) {
137-
hash_for_each_possible(card->ip_mc_htable, addr,
138-
hnode, qeth_l3_ipaddr_hash(tmp_addr))
139-
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
140-
return addr;
141-
} else {
142-
hash_for_each_possible(card->ip_htable, addr,
143-
hnode, qeth_l3_ipaddr_hash(tmp_addr))
144-
if (qeth_l3_ipaddrs_is_equal(tmp_addr, addr))
145-
return addr;
146-
}
147-
148-
return NULL;
149-
}
150-
151141
int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
152142
{
153143
int rc = 0;
@@ -162,8 +152,8 @@ int qeth_l3_delete_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
162152
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
163153
}
164154

165-
addr = qeth_l3_ip_from_hash(card, tmp_addr);
166-
if (!addr)
155+
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
156+
if (!addr || !qeth_l3_addr_match_all(addr, tmp_addr))
167157
return -ENOENT;
168158

169159
addr->ref_counter--;
@@ -185,6 +175,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
185175
{
186176
int rc = 0;
187177
struct qeth_ipaddr *addr;
178+
char buf[40];
188179

189180
QETH_CARD_TEXT(card, 4, "addip");
190181

@@ -195,8 +186,20 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
195186
QETH_CARD_HEX(card, 4, ((char *)&tmp_addr->u.a6.addr) + 8, 8);
196187
}
197188

198-
addr = qeth_l3_ip_from_hash(card, tmp_addr);
199-
if (!addr) {
189+
addr = qeth_l3_find_addr_by_ip(card, tmp_addr);
190+
if (addr) {
191+
if (tmp_addr->type != QETH_IP_TYPE_NORMAL)
192+
return -EADDRINUSE;
193+
if (qeth_l3_addr_match_all(addr, tmp_addr)) {
194+
addr->ref_counter++;
195+
return 0;
196+
}
197+
qeth_l3_ipaddr_to_string(tmp_addr->proto, (u8 *)&tmp_addr->u,
198+
buf);
199+
dev_warn(&card->gdev->dev,
200+
"Registering IP address %s failed\n", buf);
201+
return -EADDRINUSE;
202+
} else {
200203
addr = qeth_l3_get_addr_buffer(tmp_addr->proto);
201204
if (!addr)
202205
return -ENOMEM;
@@ -244,11 +247,7 @@ int qeth_l3_add_ip(struct qeth_card *card, struct qeth_ipaddr *tmp_addr)
244247
hash_del(&addr->hnode);
245248
kfree(addr);
246249
}
247-
} else {
248-
if (addr->type == QETH_IP_TYPE_NORMAL)
249-
addr->ref_counter++;
250250
}
251-
252251
return rc;
253252
}
254253

@@ -634,12 +633,7 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
634633
return -ENOMEM;
635634

636635
spin_lock_bh(&card->ip_lock);
637-
638-
if (qeth_l3_ip_from_hash(card, ipaddr))
639-
rc = -EEXIST;
640-
else
641-
rc = qeth_l3_add_ip(card, ipaddr);
642-
636+
rc = qeth_l3_add_ip(card, ipaddr);
643637
spin_unlock_bh(&card->ip_lock);
644638

645639
kfree(ipaddr);
@@ -704,12 +698,7 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
704698
return -ENOMEM;
705699

706700
spin_lock_bh(&card->ip_lock);
707-
708-
if (qeth_l3_ip_from_hash(card, ipaddr))
709-
rc = -EEXIST;
710-
else
711-
rc = qeth_l3_add_ip(card, ipaddr);
712-
701+
rc = qeth_l3_add_ip(card, ipaddr);
713702
spin_unlock_bh(&card->ip_lock);
714703

715704
kfree(ipaddr);
@@ -1230,8 +1219,9 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev)
12301219
tmp->u.a4.addr = be32_to_cpu(im4->multiaddr);
12311220
tmp->is_multicast = 1;
12321221

1233-
ipm = qeth_l3_ip_from_hash(card, tmp);
1222+
ipm = qeth_l3_find_addr_by_ip(card, tmp);
12341223
if (ipm) {
1224+
/* for mcast, by-IP match means full match */
12351225
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
12361226
} else {
12371227
ipm = qeth_l3_get_addr_buffer(QETH_PROT_IPV4);
@@ -1310,8 +1300,9 @@ static void qeth_l3_add_mc6_to_hash(struct qeth_card *card,
13101300
sizeof(struct in6_addr));
13111301
tmp->is_multicast = 1;
13121302

1313-
ipm = qeth_l3_ip_from_hash(card, tmp);
1303+
ipm = qeth_l3_find_addr_by_ip(card, tmp);
13141304
if (ipm) {
1305+
/* for mcast, by-IP match means full match */
13151306
ipm->disp_flag = QETH_DISP_ADDR_DO_NOTHING;
13161307
continue;
13171308
}

0 commit comments

Comments
 (0)