Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
4a71df50 | 2 | /* |
4a71df50 FB |
3 | * Copyright IBM Corp. 2007 |
4 | * Author(s): Utz Bacher <utz.bacher@de.ibm.com>, | |
5 | * Frank Pavlic <fpavlic@de.ibm.com>, | |
6 | * Thomas Spatzier <tspat@de.ibm.com>, | |
7 | * Frank Blaschka <frank.blaschka@de.ibm.com> | |
8 | */ | |
9 | ||
5a0e3ad6 | 10 | #include <linux/slab.h> |
b3332930 | 11 | #include <asm/ebcdic.h> |
5f78e29c | 12 | #include <linux/hashtable.h> |
556fd271 | 13 | #include <linux/inet.h> |
4a71df50 FB |
14 | #include "qeth_l3.h" |
15 | ||
16 | #define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ | |
17 | struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) | |
18 | ||
556fd271 JW |
19 | static int qeth_l3_string_to_ipaddr(const char *buf, |
20 | enum qeth_prot_versions proto, u8 *addr) | |
21 | { | |
22 | const char *end; | |
23 | ||
24 | if ((proto == QETH_PROT_IPV4 && !in4_pton(buf, -1, addr, -1, &end)) || | |
25 | (proto == QETH_PROT_IPV6 && !in6_pton(buf, -1, addr, -1, &end))) | |
26 | return -EINVAL; | |
27 | return 0; | |
28 | } | |
29 | ||
4a71df50 FB |
30 | static ssize_t qeth_l3_dev_route_show(struct qeth_card *card, |
31 | struct qeth_routing_info *route, char *buf) | |
32 | { | |
33 | switch (route->type) { | |
34 | case PRIMARY_ROUTER: | |
35 | return sprintf(buf, "%s\n", "primary router"); | |
36 | case SECONDARY_ROUTER: | |
37 | return sprintf(buf, "%s\n", "secondary router"); | |
38 | case MULTICAST_ROUTER: | |
39 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
40 | return sprintf(buf, "%s\n", "multicast router+"); | |
41 | else | |
42 | return sprintf(buf, "%s\n", "multicast router"); | |
43 | case PRIMARY_CONNECTOR: | |
44 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
45 | return sprintf(buf, "%s\n", "primary connector+"); | |
46 | else | |
47 | return sprintf(buf, "%s\n", "primary connector"); | |
48 | case SECONDARY_CONNECTOR: | |
49 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) | |
50 | return sprintf(buf, "%s\n", "secondary connector+"); | |
51 | else | |
52 | return sprintf(buf, "%s\n", "secondary connector"); | |
53 | default: | |
54 | return sprintf(buf, "%s\n", "no"); | |
55 | } | |
56 | } | |
57 | ||
58 | static ssize_t qeth_l3_dev_route4_show(struct device *dev, | |
59 | struct device_attribute *attr, char *buf) | |
60 | { | |
61 | struct qeth_card *card = dev_get_drvdata(dev); | |
62 | ||
4a71df50 FB |
63 | return qeth_l3_dev_route_show(card, &card->options.route4, buf); |
64 | } | |
65 | ||
66 | static ssize_t qeth_l3_dev_route_store(struct qeth_card *card, | |
67 | struct qeth_routing_info *route, enum qeth_prot_versions prot, | |
68 | const char *buf, size_t count) | |
69 | { | |
70 | enum qeth_routing_types old_route_type = route->type; | |
c4949f07 | 71 | int rc = 0; |
4a71df50 | 72 | |
c4949f07 | 73 | mutex_lock(&card->conf_mutex); |
7e846d6b | 74 | if (sysfs_streq(buf, "no_router")) { |
4a71df50 | 75 | route->type = NO_ROUTER; |
7e846d6b | 76 | } else if (sysfs_streq(buf, "primary_connector")) { |
4a71df50 | 77 | route->type = PRIMARY_CONNECTOR; |
7e846d6b | 78 | } else if (sysfs_streq(buf, "secondary_connector")) { |
4a71df50 | 79 | route->type = SECONDARY_CONNECTOR; |
7e846d6b | 80 | } else if (sysfs_streq(buf, "primary_router")) { |
4a71df50 | 81 | route->type = PRIMARY_ROUTER; |
7e846d6b | 82 | } else if (sysfs_streq(buf, "secondary_router")) { |
4a71df50 | 83 | route->type = SECONDARY_ROUTER; |
7e846d6b | 84 | } else if (sysfs_streq(buf, "multicast_router")) { |
4a71df50 FB |
85 | route->type = MULTICAST_ROUTER; |
86 | } else { | |
c4949f07 FB |
87 | rc = -EINVAL; |
88 | goto out; | |
4a71df50 | 89 | } |
c3521254 | 90 | if (qeth_card_hw_is_reachable(card) && |
4a71df50 FB |
91 | (old_route_type != route->type)) { |
92 | if (prot == QETH_PROT_IPV4) | |
93 | rc = qeth_l3_setrouting_v4(card); | |
94 | else if (prot == QETH_PROT_IPV6) | |
95 | rc = qeth_l3_setrouting_v6(card); | |
96 | } | |
c4949f07 | 97 | out: |
82e2e782 SR |
98 | if (rc) |
99 | route->type = old_route_type; | |
c4949f07 FB |
100 | mutex_unlock(&card->conf_mutex); |
101 | return rc ? rc : count; | |
4a71df50 FB |
102 | } |
103 | ||
104 | static ssize_t qeth_l3_dev_route4_store(struct device *dev, | |
105 | struct device_attribute *attr, const char *buf, size_t count) | |
106 | { | |
107 | struct qeth_card *card = dev_get_drvdata(dev); | |
108 | ||
4a71df50 FB |
109 | return qeth_l3_dev_route_store(card, &card->options.route4, |
110 | QETH_PROT_IPV4, buf, count); | |
111 | } | |
112 | ||
113 | static DEVICE_ATTR(route4, 0644, qeth_l3_dev_route4_show, | |
114 | qeth_l3_dev_route4_store); | |
115 | ||
116 | static ssize_t qeth_l3_dev_route6_show(struct device *dev, | |
117 | struct device_attribute *attr, char *buf) | |
118 | { | |
119 | struct qeth_card *card = dev_get_drvdata(dev); | |
120 | ||
4a71df50 FB |
121 | return qeth_l3_dev_route_show(card, &card->options.route6, buf); |
122 | } | |
123 | ||
124 | static ssize_t qeth_l3_dev_route6_store(struct device *dev, | |
125 | struct device_attribute *attr, const char *buf, size_t count) | |
126 | { | |
127 | struct qeth_card *card = dev_get_drvdata(dev); | |
128 | ||
4a71df50 FB |
129 | return qeth_l3_dev_route_store(card, &card->options.route6, |
130 | QETH_PROT_IPV6, buf, count); | |
131 | } | |
132 | ||
133 | static DEVICE_ATTR(route6, 0644, qeth_l3_dev_route6_show, | |
134 | qeth_l3_dev_route6_store); | |
135 | ||
136 | static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev, | |
137 | struct device_attribute *attr, char *buf) | |
138 | { | |
139 | struct qeth_card *card = dev_get_drvdata(dev); | |
140 | ||
4a71df50 FB |
141 | return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0); |
142 | } | |
143 | ||
144 | static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev, | |
145 | struct device_attribute *attr, const char *buf, size_t count) | |
146 | { | |
147 | struct qeth_card *card = dev_get_drvdata(dev); | |
148 | char *tmp; | |
c4949f07 | 149 | int i, rc = 0; |
4a71df50 | 150 | |
c4949f07 | 151 | mutex_lock(&card->conf_mutex); |
d7d543f2 | 152 | if (card->state != CARD_STATE_DOWN) { |
c4949f07 FB |
153 | rc = -EPERM; |
154 | goto out; | |
155 | } | |
4a71df50 FB |
156 | |
157 | i = simple_strtoul(buf, &tmp, 16); | |
158 | if ((i == 0) || (i == 1)) | |
159 | card->options.fake_broadcast = i; | |
c4949f07 FB |
160 | else |
161 | rc = -EINVAL; | |
162 | out: | |
163 | mutex_unlock(&card->conf_mutex); | |
164 | return rc ? rc : count; | |
4a71df50 FB |
165 | } |
166 | ||
167 | static DEVICE_ATTR(fake_broadcast, 0644, qeth_l3_dev_fake_broadcast_show, | |
168 | qeth_l3_dev_fake_broadcast_store); | |
169 | ||
76b11f8e UB |
170 | static ssize_t qeth_l3_dev_sniffer_show(struct device *dev, |
171 | struct device_attribute *attr, char *buf) | |
172 | { | |
173 | struct qeth_card *card = dev_get_drvdata(dev); | |
174 | ||
76b11f8e UB |
175 | return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0); |
176 | } | |
177 | ||
178 | static ssize_t qeth_l3_dev_sniffer_store(struct device *dev, | |
179 | struct device_attribute *attr, const char *buf, size_t count) | |
180 | { | |
181 | struct qeth_card *card = dev_get_drvdata(dev); | |
c4949f07 | 182 | int rc = 0; |
76b11f8e UB |
183 | unsigned long i; |
184 | ||
379ac99e | 185 | if (!IS_IQD(card)) |
76b11f8e | 186 | return -EPERM; |
b3332930 FB |
187 | if (card->options.cq == QETH_CQ_ENABLED) |
188 | return -EPERM; | |
76b11f8e | 189 | |
c4949f07 | 190 | mutex_lock(&card->conf_mutex); |
d7d543f2 | 191 | if (card->state != CARD_STATE_DOWN) { |
c4949f07 FB |
192 | rc = -EPERM; |
193 | goto out; | |
194 | } | |
76b11f8e | 195 | |
0178722b | 196 | rc = kstrtoul(buf, 16, &i); |
c4949f07 FB |
197 | if (rc) { |
198 | rc = -EINVAL; | |
199 | goto out; | |
200 | } | |
76b11f8e UB |
201 | switch (i) { |
202 | case 0: | |
203 | card->options.sniffer = i; | |
204 | break; | |
205 | case 1: | |
c4949f07 | 206 | qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd); |
1917b47d | 207 | if (card->ssqd.qdioac2 & CHSC_AC2_SNIFFER_AVAILABLE) { |
76b11f8e | 208 | card->options.sniffer = i; |
5d4f7856 JW |
209 | qeth_resize_buffer_pool(card, QETH_IN_BUF_COUNT_MAX); |
210 | } else { | |
c4949f07 | 211 | rc = -EPERM; |
5d4f7856 JW |
212 | } |
213 | ||
6cc31d09 UB |
214 | break; |
215 | default: | |
c4949f07 | 216 | rc = -EINVAL; |
76b11f8e | 217 | } |
c4949f07 FB |
218 | out: |
219 | mutex_unlock(&card->conf_mutex); | |
220 | return rc ? rc : count; | |
76b11f8e UB |
221 | } |
222 | ||
223 | static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show, | |
224 | qeth_l3_dev_sniffer_store); | |
225 | ||
b3332930 FB |
226 | static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, |
227 | struct device_attribute *attr, char *buf) | |
228 | { | |
229 | struct qeth_card *card = dev_get_drvdata(dev); | |
230 | char tmp_hsuid[9]; | |
231 | ||
379ac99e | 232 | if (!IS_IQD(card)) |
b3332930 FB |
233 | return -EPERM; |
234 | ||
b3332930 FB |
235 | memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid)); |
236 | EBCASC(tmp_hsuid, 8); | |
237 | return sprintf(buf, "%s\n", tmp_hsuid); | |
238 | } | |
239 | ||
240 | static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, | |
241 | struct device_attribute *attr, const char *buf, size_t count) | |
242 | { | |
243 | struct qeth_card *card = dev_get_drvdata(dev); | |
5b6c7b55 | 244 | int rc = 0; |
b3332930 | 245 | char *tmp; |
b3332930 | 246 | |
379ac99e | 247 | if (!IS_IQD(card)) |
b3332930 | 248 | return -EPERM; |
5b6c7b55 JW |
249 | |
250 | mutex_lock(&card->conf_mutex); | |
251 | if (card->state != CARD_STATE_DOWN) { | |
252 | rc = -EPERM; | |
253 | goto out; | |
254 | } | |
255 | ||
256 | if (card->options.sniffer) { | |
257 | rc = -EPERM; | |
258 | goto out; | |
259 | } | |
260 | ||
261 | if (card->options.cq == QETH_CQ_NOTAVAILABLE) { | |
262 | rc = -EPERM; | |
263 | goto out; | |
264 | } | |
b3332930 FB |
265 | |
266 | tmp = strsep((char **)&buf, "\n"); | |
5b6c7b55 JW |
267 | if (strlen(tmp) > 8) { |
268 | rc = -EINVAL; | |
269 | goto out; | |
270 | } | |
b3332930 | 271 | |
1617dae2 | 272 | if (card->options.hsuid[0]) |
b3332930 | 273 | /* delete old ip address */ |
1617dae2 | 274 | qeth_l3_modify_hsuid(card, false); |
b3332930 FB |
275 | |
276 | if (strlen(tmp) == 0) { | |
277 | /* delete ip address only */ | |
278 | card->options.hsuid[0] = '\0'; | |
d3d1b205 | 279 | memcpy(card->dev->perm_addr, card->options.hsuid, 9); |
b3332930 | 280 | qeth_configure_cq(card, QETH_CQ_DISABLED); |
5b6c7b55 | 281 | goto out; |
b3332930 FB |
282 | } |
283 | ||
5b6c7b55 JW |
284 | if (qeth_configure_cq(card, QETH_CQ_ENABLED)) { |
285 | rc = -EPERM; | |
286 | goto out; | |
287 | } | |
b3332930 | 288 | |
096a8aac KC |
289 | snprintf(card->options.hsuid, sizeof(card->options.hsuid), |
290 | "%-8s", tmp); | |
b3332930 | 291 | ASCEBC(card->options.hsuid, 8); |
d3d1b205 | 292 | memcpy(card->dev->perm_addr, card->options.hsuid, 9); |
b3332930 | 293 | |
1617dae2 | 294 | rc = qeth_l3_modify_hsuid(card, true); |
b3332930 | 295 | |
5b6c7b55 JW |
296 | out: |
297 | mutex_unlock(&card->conf_mutex); | |
b9ea5250 | 298 | return rc ? rc : count; |
b3332930 FB |
299 | } |
300 | ||
301 | static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show, | |
302 | qeth_l3_dev_hsuid_store); | |
303 | ||
304 | ||
4a71df50 FB |
305 | static struct attribute *qeth_l3_device_attrs[] = { |
306 | &dev_attr_route4.attr, | |
307 | &dev_attr_route6.attr, | |
308 | &dev_attr_fake_broadcast.attr, | |
76b11f8e | 309 | &dev_attr_sniffer.attr, |
b3332930 | 310 | &dev_attr_hsuid.attr, |
4a71df50 FB |
311 | NULL, |
312 | }; | |
313 | ||
cfe9a042 | 314 | static const struct attribute_group qeth_l3_device_attr_group = { |
4a71df50 FB |
315 | .attrs = qeth_l3_device_attrs, |
316 | }; | |
317 | ||
318 | static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev, | |
319 | struct device_attribute *attr, char *buf) | |
320 | { | |
321 | struct qeth_card *card = dev_get_drvdata(dev); | |
322 | ||
4a71df50 FB |
323 | return sprintf(buf, "%i\n", card->ipato.enabled? 1:0); |
324 | } | |
325 | ||
326 | static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, | |
327 | struct device_attribute *attr, const char *buf, size_t count) | |
328 | { | |
329 | struct qeth_card *card = dev_get_drvdata(dev); | |
7fbd9493 | 330 | bool enable; |
02f510f3 | 331 | int rc = 0; |
4a71df50 | 332 | |
c4949f07 | 333 | mutex_lock(&card->conf_mutex); |
d7d543f2 | 334 | if (card->state != CARD_STATE_DOWN) { |
c4949f07 FB |
335 | rc = -EPERM; |
336 | goto out; | |
337 | } | |
4a71df50 | 338 | |
7e846d6b | 339 | if (sysfs_streq(buf, "toggle")) { |
7fbd9493 JW |
340 | enable = !card->ipato.enabled; |
341 | } else if (kstrtobool(buf, &enable)) { | |
c4949f07 | 342 | rc = -EINVAL; |
7fbd9493 JW |
343 | goto out; |
344 | } | |
345 | ||
02f510f3 JW |
346 | if (card->ipato.enabled != enable) { |
347 | card->ipato.enabled = enable; | |
df2a2a52 | 348 | mutex_lock(&card->ip_lock); |
02f510f3 | 349 | qeth_l3_update_ipato(card); |
df2a2a52 | 350 | mutex_unlock(&card->ip_lock); |
7fbd9493 | 351 | } |
c4949f07 FB |
352 | out: |
353 | mutex_unlock(&card->conf_mutex); | |
354 | return rc ? rc : count; | |
4a71df50 FB |
355 | } |
356 | ||
357 | static QETH_DEVICE_ATTR(ipato_enable, enable, 0644, | |
358 | qeth_l3_dev_ipato_enable_show, | |
359 | qeth_l3_dev_ipato_enable_store); | |
360 | ||
361 | static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev, | |
362 | struct device_attribute *attr, char *buf) | |
363 | { | |
364 | struct qeth_card *card = dev_get_drvdata(dev); | |
365 | ||
4a71df50 FB |
366 | return sprintf(buf, "%i\n", card->ipato.invert4? 1:0); |
367 | } | |
368 | ||
369 | static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev, | |
370 | struct device_attribute *attr, | |
371 | const char *buf, size_t count) | |
372 | { | |
373 | struct qeth_card *card = dev_get_drvdata(dev); | |
02f510f3 | 374 | bool invert; |
c4949f07 | 375 | int rc = 0; |
4a71df50 | 376 | |
c4949f07 | 377 | mutex_lock(&card->conf_mutex); |
02f510f3 JW |
378 | if (sysfs_streq(buf, "toggle")) { |
379 | invert = !card->ipato.invert4; | |
380 | } else if (kstrtobool(buf, &invert)) { | |
c4949f07 | 381 | rc = -EINVAL; |
02f510f3 JW |
382 | goto out; |
383 | } | |
384 | ||
385 | if (card->ipato.invert4 != invert) { | |
386 | card->ipato.invert4 = invert; | |
df2a2a52 | 387 | mutex_lock(&card->ip_lock); |
02f510f3 | 388 | qeth_l3_update_ipato(card); |
df2a2a52 | 389 | mutex_unlock(&card->ip_lock); |
02f510f3 JW |
390 | } |
391 | out: | |
c4949f07 FB |
392 | mutex_unlock(&card->conf_mutex); |
393 | return rc ? rc : count; | |
4a71df50 FB |
394 | } |
395 | ||
396 | static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644, | |
397 | qeth_l3_dev_ipato_invert4_show, | |
398 | qeth_l3_dev_ipato_invert4_store); | |
399 | ||
400 | static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card, | |
401 | enum qeth_prot_versions proto) | |
402 | { | |
403 | struct qeth_ipato_entry *ipatoe; | |
e6b1b7da | 404 | int str_len = 0; |
4a71df50 | 405 | |
df2a2a52 | 406 | mutex_lock(&card->ip_lock); |
4a71df50 | 407 | list_for_each_entry(ipatoe, &card->ipato.entries, entry) { |
e6b1b7da JW |
408 | char addr_str[40]; |
409 | int entry_len; | |
410 | ||
4a71df50 FB |
411 | if (ipatoe->proto != proto) |
412 | continue; | |
e6b1b7da JW |
413 | |
414 | entry_len = qeth_l3_ipaddr_to_string(proto, ipatoe->addr, | |
415 | addr_str); | |
416 | if (entry_len < 0) | |
417 | continue; | |
418 | ||
419 | /* Append /%mask to the entry: */ | |
420 | entry_len += 1 + ((proto == QETH_PROT_IPV4) ? 2 : 3); | |
421 | /* Enough room to format %entry\n into null terminated page? */ | |
422 | if (entry_len + 1 > PAGE_SIZE - str_len - 1) | |
4a71df50 | 423 | break; |
e6b1b7da JW |
424 | |
425 | entry_len = scnprintf(buf, PAGE_SIZE - str_len, | |
426 | "%s/%i\n", addr_str, ipatoe->mask_bits); | |
427 | str_len += entry_len; | |
428 | buf += entry_len; | |
4a71df50 | 429 | } |
df2a2a52 | 430 | mutex_unlock(&card->ip_lock); |
4a71df50 | 431 | |
e6b1b7da | 432 | return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); |
4a71df50 FB |
433 | } |
434 | ||
435 | static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev, | |
436 | struct device_attribute *attr, char *buf) | |
437 | { | |
438 | struct qeth_card *card = dev_get_drvdata(dev); | |
439 | ||
4a71df50 FB |
440 | return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4); |
441 | } | |
442 | ||
443 | static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto, | |
444 | u8 *addr, int *mask_bits) | |
445 | { | |
446 | const char *start, *end; | |
447 | char *tmp; | |
448 | char buffer[40] = {0, }; | |
449 | ||
450 | start = buf; | |
451 | /* get address string */ | |
452 | end = strchr(start, '/'); | |
453 | if (!end || (end - start >= 40)) { | |
4a71df50 FB |
454 | return -EINVAL; |
455 | } | |
456 | strncpy(buffer, start, end - start); | |
457 | if (qeth_l3_string_to_ipaddr(buffer, proto, addr)) { | |
4a71df50 FB |
458 | return -EINVAL; |
459 | } | |
460 | start = end + 1; | |
461 | *mask_bits = simple_strtoul(start, &tmp, 10); | |
462 | if (!strlen(start) || | |
463 | (tmp == start) || | |
464 | (*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) { | |
4a71df50 FB |
465 | return -EINVAL; |
466 | } | |
467 | return 0; | |
468 | } | |
469 | ||
470 | static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count, | |
471 | struct qeth_card *card, enum qeth_prot_versions proto) | |
472 | { | |
473 | struct qeth_ipato_entry *ipatoe; | |
474 | u8 addr[16]; | |
475 | int mask_bits; | |
c4949f07 | 476 | int rc = 0; |
4a71df50 FB |
477 | |
478 | rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); | |
479 | if (rc) | |
2390166a | 480 | return rc; |
4a71df50 FB |
481 | |
482 | ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL); | |
2390166a JW |
483 | if (!ipatoe) |
484 | return -ENOMEM; | |
485 | ||
4a71df50 FB |
486 | ipatoe->proto = proto; |
487 | memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16); | |
488 | ipatoe->mask_bits = mask_bits; | |
489 | ||
490 | rc = qeth_l3_add_ipato_entry(card, ipatoe); | |
c4949f07 | 491 | if (rc) |
4a71df50 | 492 | kfree(ipatoe); |
2390166a | 493 | |
c4949f07 | 494 | return rc ? rc : count; |
4a71df50 FB |
495 | } |
496 | ||
497 | static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev, | |
498 | struct device_attribute *attr, const char *buf, size_t count) | |
499 | { | |
500 | struct qeth_card *card = dev_get_drvdata(dev); | |
501 | ||
4a71df50 FB |
502 | return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4); |
503 | } | |
504 | ||
505 | static QETH_DEVICE_ATTR(ipato_add4, add4, 0644, | |
506 | qeth_l3_dev_ipato_add4_show, | |
507 | qeth_l3_dev_ipato_add4_store); | |
508 | ||
509 | static ssize_t qeth_l3_dev_ipato_del_store(const char *buf, size_t count, | |
510 | struct qeth_card *card, enum qeth_prot_versions proto) | |
511 | { | |
512 | u8 addr[16]; | |
513 | int mask_bits; | |
c4949f07 | 514 | int rc = 0; |
4a71df50 FB |
515 | |
516 | rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits); | |
c4949f07 | 517 | if (!rc) |
b9ea5250 | 518 | rc = qeth_l3_del_ipato_entry(card, proto, addr, mask_bits); |
c4949f07 | 519 | return rc ? rc : count; |
4a71df50 FB |
520 | } |
521 | ||
522 | static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev, | |
523 | struct device_attribute *attr, const char *buf, size_t count) | |
524 | { | |
525 | struct qeth_card *card = dev_get_drvdata(dev); | |
526 | ||
4a71df50 FB |
527 | return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4); |
528 | } | |
529 | ||
530 | static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL, | |
531 | qeth_l3_dev_ipato_del4_store); | |
532 | ||
533 | static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev, | |
534 | struct device_attribute *attr, char *buf) | |
535 | { | |
536 | struct qeth_card *card = dev_get_drvdata(dev); | |
537 | ||
4a71df50 FB |
538 | return sprintf(buf, "%i\n", card->ipato.invert6? 1:0); |
539 | } | |
540 | ||
541 | static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev, | |
542 | struct device_attribute *attr, const char *buf, size_t count) | |
543 | { | |
544 | struct qeth_card *card = dev_get_drvdata(dev); | |
02f510f3 | 545 | bool invert; |
c4949f07 | 546 | int rc = 0; |
4a71df50 | 547 | |
c4949f07 | 548 | mutex_lock(&card->conf_mutex); |
02f510f3 JW |
549 | if (sysfs_streq(buf, "toggle")) { |
550 | invert = !card->ipato.invert6; | |
551 | } else if (kstrtobool(buf, &invert)) { | |
c4949f07 | 552 | rc = -EINVAL; |
02f510f3 JW |
553 | goto out; |
554 | } | |
555 | ||
556 | if (card->ipato.invert6 != invert) { | |
557 | card->ipato.invert6 = invert; | |
df2a2a52 | 558 | mutex_lock(&card->ip_lock); |
02f510f3 | 559 | qeth_l3_update_ipato(card); |
df2a2a52 | 560 | mutex_unlock(&card->ip_lock); |
02f510f3 JW |
561 | } |
562 | out: | |
c4949f07 FB |
563 | mutex_unlock(&card->conf_mutex); |
564 | return rc ? rc : count; | |
4a71df50 FB |
565 | } |
566 | ||
567 | static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644, | |
568 | qeth_l3_dev_ipato_invert6_show, | |
569 | qeth_l3_dev_ipato_invert6_store); | |
570 | ||
571 | ||
572 | static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev, | |
573 | struct device_attribute *attr, char *buf) | |
574 | { | |
575 | struct qeth_card *card = dev_get_drvdata(dev); | |
576 | ||
4a71df50 FB |
577 | return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6); |
578 | } | |
579 | ||
580 | static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev, | |
581 | struct device_attribute *attr, const char *buf, size_t count) | |
582 | { | |
583 | struct qeth_card *card = dev_get_drvdata(dev); | |
584 | ||
4a71df50 FB |
585 | return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6); |
586 | } | |
587 | ||
588 | static QETH_DEVICE_ATTR(ipato_add6, add6, 0644, | |
589 | qeth_l3_dev_ipato_add6_show, | |
590 | qeth_l3_dev_ipato_add6_store); | |
591 | ||
592 | static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev, | |
593 | struct device_attribute *attr, const char *buf, size_t count) | |
594 | { | |
595 | struct qeth_card *card = dev_get_drvdata(dev); | |
596 | ||
4a71df50 FB |
597 | return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6); |
598 | } | |
599 | ||
600 | static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL, | |
601 | qeth_l3_dev_ipato_del6_store); | |
602 | ||
603 | static struct attribute *qeth_ipato_device_attrs[] = { | |
604 | &dev_attr_ipato_enable.attr, | |
605 | &dev_attr_ipato_invert4.attr, | |
606 | &dev_attr_ipato_add4.attr, | |
607 | &dev_attr_ipato_del4.attr, | |
608 | &dev_attr_ipato_invert6.attr, | |
609 | &dev_attr_ipato_add6.attr, | |
610 | &dev_attr_ipato_del6.attr, | |
611 | NULL, | |
612 | }; | |
613 | ||
cfe9a042 | 614 | static const struct attribute_group qeth_device_ipato_group = { |
4a71df50 FB |
615 | .name = "ipa_takeover", |
616 | .attrs = qeth_ipato_device_attrs, | |
617 | }; | |
618 | ||
d2073ef3 JW |
619 | static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf, |
620 | enum qeth_prot_versions proto, | |
621 | enum qeth_ip_types type) | |
4a71df50 | 622 | { |
d2073ef3 | 623 | struct qeth_card *card = dev_get_drvdata(dev); |
4a71df50 | 624 | struct qeth_ipaddr *ipaddr; |
e48b9eaa | 625 | int str_len = 0; |
e48b9eaa | 626 | int i; |
4a71df50 | 627 | |
df2a2a52 | 628 | mutex_lock(&card->ip_lock); |
e48b9eaa | 629 | hash_for_each(card->ip_htable, i, ipaddr, hnode) { |
e6b1b7da JW |
630 | char addr_str[40]; |
631 | int entry_len; | |
632 | ||
d2073ef3 | 633 | if (ipaddr->proto != proto || ipaddr->type != type) |
4a71df50 | 634 | continue; |
e6b1b7da JW |
635 | |
636 | entry_len = qeth_l3_ipaddr_to_string(proto, (u8 *)&ipaddr->u, | |
637 | addr_str); | |
638 | if (entry_len < 0) | |
639 | continue; | |
640 | ||
641 | /* Enough room to format %addr\n into null terminated page? */ | |
642 | if (entry_len + 1 > PAGE_SIZE - str_len - 1) | |
4a71df50 | 643 | break; |
e6b1b7da JW |
644 | |
645 | entry_len = scnprintf(buf, PAGE_SIZE - str_len, "%s\n", | |
646 | addr_str); | |
647 | str_len += entry_len; | |
648 | buf += entry_len; | |
4a71df50 | 649 | } |
df2a2a52 | 650 | mutex_unlock(&card->ip_lock); |
4a71df50 | 651 | |
e6b1b7da | 652 | return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n"); |
4a71df50 FB |
653 | } |
654 | ||
655 | static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, | |
d2073ef3 JW |
656 | struct device_attribute *attr, |
657 | char *buf) | |
4a71df50 | 658 | { |
d2073ef3 JW |
659 | return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV4, |
660 | QETH_IP_TYPE_VIPA); | |
4a71df50 FB |
661 | } |
662 | ||
2390166a JW |
663 | static ssize_t qeth_l3_vipa_store(struct device *dev, const char *buf, bool add, |
664 | size_t count, enum qeth_prot_versions proto) | |
4a71df50 | 665 | { |
2390166a | 666 | struct qeth_card *card = dev_get_drvdata(dev); |
4a71df50 FB |
667 | u8 addr[16] = {0, }; |
668 | int rc; | |
669 | ||
2390166a | 670 | rc = qeth_l3_string_to_ipaddr(buf, proto, addr); |
c4949f07 | 671 | if (!rc) |
2390166a | 672 | rc = qeth_l3_modify_rxip_vipa(card, add, addr, |
1617dae2 | 673 | QETH_IP_TYPE_VIPA, proto); |
c4949f07 | 674 | return rc ? rc : count; |
4a71df50 FB |
675 | } |
676 | ||
677 | static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev, | |
678 | struct device_attribute *attr, const char *buf, size_t count) | |
679 | { | |
2390166a | 680 | return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); |
4a71df50 FB |
681 | } |
682 | ||
683 | static QETH_DEVICE_ATTR(vipa_add4, add4, 0644, | |
684 | qeth_l3_dev_vipa_add4_show, | |
685 | qeth_l3_dev_vipa_add4_store); | |
686 | ||
4a71df50 FB |
687 | static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev, |
688 | struct device_attribute *attr, const char *buf, size_t count) | |
689 | { | |
2390166a | 690 | return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV4); |
4a71df50 FB |
691 | } |
692 | ||
693 | static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL, | |
694 | qeth_l3_dev_vipa_del4_store); | |
695 | ||
696 | static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev, | |
d2073ef3 JW |
697 | struct device_attribute *attr, |
698 | char *buf) | |
4a71df50 | 699 | { |
d2073ef3 JW |
700 | return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV6, |
701 | QETH_IP_TYPE_VIPA); | |
4a71df50 FB |
702 | } |
703 | ||
704 | static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev, | |
705 | struct device_attribute *attr, const char *buf, size_t count) | |
706 | { | |
2390166a | 707 | return qeth_l3_vipa_store(dev, buf, true, count, QETH_PROT_IPV6); |
4a71df50 FB |
708 | } |
709 | ||
710 | static QETH_DEVICE_ATTR(vipa_add6, add6, 0644, | |
711 | qeth_l3_dev_vipa_add6_show, | |
712 | qeth_l3_dev_vipa_add6_store); | |
713 | ||
714 | static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev, | |
715 | struct device_attribute *attr, const char *buf, size_t count) | |
716 | { | |
2390166a | 717 | return qeth_l3_vipa_store(dev, buf, false, count, QETH_PROT_IPV6); |
4a71df50 FB |
718 | } |
719 | ||
720 | static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL, | |
721 | qeth_l3_dev_vipa_del6_store); | |
722 | ||
723 | static struct attribute *qeth_vipa_device_attrs[] = { | |
724 | &dev_attr_vipa_add4.attr, | |
725 | &dev_attr_vipa_del4.attr, | |
726 | &dev_attr_vipa_add6.attr, | |
727 | &dev_attr_vipa_del6.attr, | |
728 | NULL, | |
729 | }; | |
730 | ||
cfe9a042 | 731 | static const struct attribute_group qeth_device_vipa_group = { |
4a71df50 FB |
732 | .name = "vipa", |
733 | .attrs = qeth_vipa_device_attrs, | |
734 | }; | |
735 | ||
4a71df50 | 736 | static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev, |
d2073ef3 JW |
737 | struct device_attribute *attr, |
738 | char *buf) | |
4a71df50 | 739 | { |
d2073ef3 JW |
740 | return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV4, |
741 | QETH_IP_TYPE_RXIP); | |
4a71df50 FB |
742 | } |
743 | ||
744 | static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto, | |
745 | u8 *addr) | |
746 | { | |
aa9bea0b KM |
747 | __be32 ipv4_addr; |
748 | struct in6_addr ipv6_addr; | |
749 | ||
4a71df50 | 750 | if (qeth_l3_string_to_ipaddr(buf, proto, addr)) { |
4a71df50 FB |
751 | return -EINVAL; |
752 | } | |
aa9bea0b KM |
753 | if (proto == QETH_PROT_IPV4) { |
754 | memcpy(&ipv4_addr, addr, sizeof(ipv4_addr)); | |
755 | if (ipv4_is_multicast(ipv4_addr)) { | |
756 | QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n"); | |
757 | return -EINVAL; | |
758 | } | |
759 | } else if (proto == QETH_PROT_IPV6) { | |
760 | memcpy(&ipv6_addr, addr, sizeof(ipv6_addr)); | |
761 | if (ipv6_addr_is_multicast(&ipv6_addr)) { | |
762 | QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n"); | |
763 | return -EINVAL; | |
764 | } | |
765 | } | |
766 | ||
4a71df50 FB |
767 | return 0; |
768 | } | |
769 | ||
2390166a JW |
770 | static ssize_t qeth_l3_rxip_store(struct device *dev, const char *buf, bool add, |
771 | size_t count, enum qeth_prot_versions proto) | |
4a71df50 | 772 | { |
2390166a | 773 | struct qeth_card *card = dev_get_drvdata(dev); |
4a71df50 FB |
774 | u8 addr[16] = {0, }; |
775 | int rc; | |
776 | ||
777 | rc = qeth_l3_parse_rxipe(buf, proto, addr); | |
c4949f07 | 778 | if (!rc) |
2390166a | 779 | rc = qeth_l3_modify_rxip_vipa(card, add, addr, |
1617dae2 | 780 | QETH_IP_TYPE_RXIP, proto); |
c4949f07 | 781 | return rc ? rc : count; |
4a71df50 FB |
782 | } |
783 | ||
784 | static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev, | |
785 | struct device_attribute *attr, const char *buf, size_t count) | |
786 | { | |
2390166a | 787 | return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV4); |
4a71df50 FB |
788 | } |
789 | ||
790 | static QETH_DEVICE_ATTR(rxip_add4, add4, 0644, | |
791 | qeth_l3_dev_rxip_add4_show, | |
792 | qeth_l3_dev_rxip_add4_store); | |
793 | ||
4a71df50 FB |
794 | static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev, |
795 | struct device_attribute *attr, const char *buf, size_t count) | |
796 | { | |
2390166a | 797 | return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV4); |
4a71df50 FB |
798 | } |
799 | ||
800 | static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL, | |
801 | qeth_l3_dev_rxip_del4_store); | |
802 | ||
803 | static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev, | |
d2073ef3 JW |
804 | struct device_attribute *attr, |
805 | char *buf) | |
4a71df50 | 806 | { |
d2073ef3 JW |
807 | return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV6, |
808 | QETH_IP_TYPE_RXIP); | |
4a71df50 FB |
809 | } |
810 | ||
811 | static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev, | |
812 | struct device_attribute *attr, const char *buf, size_t count) | |
813 | { | |
2390166a | 814 | return qeth_l3_rxip_store(dev, buf, true, count, QETH_PROT_IPV6); |
4a71df50 FB |
815 | } |
816 | ||
817 | static QETH_DEVICE_ATTR(rxip_add6, add6, 0644, | |
818 | qeth_l3_dev_rxip_add6_show, | |
819 | qeth_l3_dev_rxip_add6_store); | |
820 | ||
821 | static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev, | |
822 | struct device_attribute *attr, const char *buf, size_t count) | |
823 | { | |
2390166a | 824 | return qeth_l3_rxip_store(dev, buf, false, count, QETH_PROT_IPV6); |
4a71df50 FB |
825 | } |
826 | ||
827 | static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL, | |
828 | qeth_l3_dev_rxip_del6_store); | |
829 | ||
830 | static struct attribute *qeth_rxip_device_attrs[] = { | |
831 | &dev_attr_rxip_add4.attr, | |
832 | &dev_attr_rxip_del4.attr, | |
833 | &dev_attr_rxip_add6.attr, | |
834 | &dev_attr_rxip_del6.attr, | |
835 | NULL, | |
836 | }; | |
837 | ||
cfe9a042 | 838 | static const struct attribute_group qeth_device_rxip_group = { |
4a71df50 FB |
839 | .name = "rxip", |
840 | .attrs = qeth_rxip_device_attrs, | |
841 | }; | |
842 | ||
ab25a501 JW |
843 | static const struct attribute_group *qeth_l3_only_attr_groups[] = { |
844 | &qeth_l3_device_attr_group, | |
845 | &qeth_device_ipato_group, | |
846 | &qeth_device_vipa_group, | |
847 | &qeth_device_rxip_group, | |
848 | NULL, | |
849 | }; | |
850 | ||
4a71df50 FB |
851 | int qeth_l3_create_device_attributes(struct device *dev) |
852 | { | |
ab25a501 | 853 | return sysfs_create_groups(&dev->kobj, qeth_l3_only_attr_groups); |
4a71df50 FB |
854 | } |
855 | ||
856 | void qeth_l3_remove_device_attributes(struct device *dev) | |
857 | { | |
ab25a501 | 858 | sysfs_remove_groups(&dev->kobj, qeth_l3_only_attr_groups); |
4a71df50 | 859 | } |
79a04e40 UB |
860 | |
861 | const struct attribute_group *qeth_l3_attr_groups[] = { | |
862 | &qeth_device_attr_group, | |
863 | &qeth_device_blkt_group, | |
ab25a501 | 864 | /* l3 specific, see qeth_l3_only_attr_groups: */ |
79a04e40 UB |
865 | &qeth_l3_device_attr_group, |
866 | &qeth_device_ipato_group, | |
867 | &qeth_device_vipa_group, | |
868 | &qeth_device_rxip_group, | |
ab25a501 | 869 | NULL, |
79a04e40 | 870 | }; |