linux_dsm_epyc7002/drivers/s390/net/qeth_l3_sys.c
Linus Torvalds 386403a115 Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from David Miller:
 "Another merge window, another pull full of stuff:

   1) Support alternative names for network devices, from Jiri Pirko.

   2) Introduce per-netns netdev notifiers, also from Jiri Pirko.

   3) Support MSG_PEEK in vsock/virtio, from Matias Ezequiel Vara
      Larsen.

   4) Allow compiling out the TLS TOE code, from Jakub Kicinski.

   5) Add several new tracepoints to the kTLS code, also from Jakub.

   6) Support set channels ethtool callback in ena driver, from Sameeh
      Jubran.

   7) New SCTP events SCTP_ADDR_ADDED, SCTP_ADDR_REMOVED,
      SCTP_ADDR_MADE_PRIM, and SCTP_SEND_FAILED_EVENT. From Xin Long.

   8) Add XDP support to mvneta driver, from Lorenzo Bianconi.

   9) Lots of netfilter hw offload fixes, cleanups and enhancements,
      from Pablo Neira Ayuso.

  10) PTP support for aquantia chips, from Egor Pomozov.

  11) Add UDP segmentation offload support to igb, ixgbe, and i40e. From
      Josh Hunt.

  12) Add smart nagle to tipc, from Jon Maloy.

  13) Support L2 field rewrite by TC offloads in bnxt_en, from Venkat
      Duvvuru.

  14) Add a flow mask cache to OVS, from Tonghao Zhang.

  15) Add XDP support to ice driver, from Maciej Fijalkowski.

  16) Add AF_XDP support to ice driver, from Krzysztof Kazimierczak.

  17) Support UDP GSO offload in atlantic driver, from Igor Russkikh.

  18) Support it in stmmac driver too, from Jose Abreu.

  19) Support TIPC encryption and auth, from Tuong Lien.

  20) Introduce BPF trampolines, from Alexei Starovoitov.

  21) Make page_pool API more numa friendly, from Saeed Mahameed.

  22) Introduce route hints to ipv4 and ipv6, from Paolo Abeni.

  23) Add UDP segmentation offload to cxgb4, Rahul Lakkireddy"

* git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1857 commits)
  libbpf: Fix usage of u32 in userspace code
  mm: Implement no-MMU variant of vmalloc_user_node_flags
  slip: Fix use-after-free Read in slip_open
  net: dsa: sja1105: fix sja1105_parse_rgmii_delays()
  macvlan: schedule bc_work even if error
  enetc: add support Credit Based Shaper(CBS) for hardware offload
  net: phy: add helpers phy_(un)lock_mdio_bus
  mdio_bus: don't use managed reset-controller
  ax88179_178a: add ethtool_op_get_ts_info()
  mlxsw: spectrum_router: Fix use of uninitialized adjacency index
  mlxsw: spectrum_router: After underlay moves, demote conflicting tunnels
  bpf: Simplify __bpf_arch_text_poke poke type handling
  bpf: Introduce BPF_TRACE_x helper for the tracing tests
  bpf: Add bpf_jit_blinding_enabled for !CONFIG_BPF_JIT
  bpf, testing: Add various tail call test cases
  bpf, x86: Emit patchable direct jump as tail call
  bpf: Constant map key tracking for prog array pokes
  bpf: Add poke dependency tracking for prog array maps
  bpf: Add initial poke descriptor table for jit images
  bpf: Move owner type, jited info into array auxiliary data
  ...
2019-11-25 20:02:57 -08:00

910 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright IBM Corp. 2007
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>,
* Frank Pavlic <fpavlic@de.ibm.com>,
* Thomas Spatzier <tspat@de.ibm.com>,
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
#include <linux/slab.h>
#include <asm/ebcdic.h>
#include <linux/hashtable.h>
#include <linux/inet.h>
#include "qeth_l3.h"
#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \
struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store)
static int qeth_l3_string_to_ipaddr(const char *buf,
enum qeth_prot_versions proto, u8 *addr)
{
const char *end;
if ((proto == QETH_PROT_IPV4 && !in4_pton(buf, -1, addr, -1, &end)) ||
(proto == QETH_PROT_IPV6 && !in6_pton(buf, -1, addr, -1, &end)))
return -EINVAL;
return 0;
}
static ssize_t qeth_l3_dev_route_show(struct qeth_card *card,
struct qeth_routing_info *route, char *buf)
{
switch (route->type) {
case PRIMARY_ROUTER:
return sprintf(buf, "%s\n", "primary router");
case SECONDARY_ROUTER:
return sprintf(buf, "%s\n", "secondary router");
case MULTICAST_ROUTER:
if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
return sprintf(buf, "%s\n", "multicast router+");
else
return sprintf(buf, "%s\n", "multicast router");
case PRIMARY_CONNECTOR:
if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
return sprintf(buf, "%s\n", "primary connector+");
else
return sprintf(buf, "%s\n", "primary connector");
case SECONDARY_CONNECTOR:
if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO)
return sprintf(buf, "%s\n", "secondary connector+");
else
return sprintf(buf, "%s\n", "secondary connector");
default:
return sprintf(buf, "%s\n", "no");
}
}
static ssize_t qeth_l3_dev_route4_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_route_show(card, &card->options.route4, buf);
}
static ssize_t qeth_l3_dev_route_store(struct qeth_card *card,
struct qeth_routing_info *route, enum qeth_prot_versions prot,
const char *buf, size_t count)
{
enum qeth_routing_types old_route_type = route->type;
int rc = 0;
mutex_lock(&card->conf_mutex);
if (sysfs_streq(buf, "no_router")) {
route->type = NO_ROUTER;
} else if (sysfs_streq(buf, "primary_connector")) {
route->type = PRIMARY_CONNECTOR;
} else if (sysfs_streq(buf, "secondary_connector")) {
route->type = SECONDARY_CONNECTOR;
} else if (sysfs_streq(buf, "primary_router")) {
route->type = PRIMARY_ROUTER;
} else if (sysfs_streq(buf, "secondary_router")) {
route->type = SECONDARY_ROUTER;
} else if (sysfs_streq(buf, "multicast_router")) {
route->type = MULTICAST_ROUTER;
} else {
rc = -EINVAL;
goto out;
}
if (qeth_card_hw_is_reachable(card) &&
(old_route_type != route->type)) {
if (prot == QETH_PROT_IPV4)
rc = qeth_l3_setrouting_v4(card);
else if (prot == QETH_PROT_IPV6)
rc = qeth_l3_setrouting_v6(card);
}
out:
if (rc)
route->type = old_route_type;
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_route4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_route_store(card, &card->options.route4,
QETH_PROT_IPV4, buf, count);
}
static DEVICE_ATTR(route4, 0644, qeth_l3_dev_route4_show,
qeth_l3_dev_route4_store);
static ssize_t qeth_l3_dev_route6_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_route_show(card, &card->options.route6, buf);
}
static ssize_t qeth_l3_dev_route6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_route_store(card, &card->options.route6,
QETH_PROT_IPV6, buf, count);
}
static DEVICE_ATTR(route6, 0644, qeth_l3_dev_route6_show,
qeth_l3_dev_route6_store);
static ssize_t qeth_l3_dev_fake_broadcast_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return sprintf(buf, "%i\n", card->options.fake_broadcast? 1:0);
}
static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
char *tmp;
int i, rc = 0;
mutex_lock(&card->conf_mutex);
if (card->state != CARD_STATE_DOWN) {
rc = -EPERM;
goto out;
}
i = simple_strtoul(buf, &tmp, 16);
if ((i == 0) || (i == 1))
card->options.fake_broadcast = i;
else
rc = -EINVAL;
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static DEVICE_ATTR(fake_broadcast, 0644, qeth_l3_dev_fake_broadcast_show,
qeth_l3_dev_fake_broadcast_store);
static ssize_t qeth_l3_dev_sniffer_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return sprintf(buf, "%i\n", card->options.sniffer ? 1 : 0);
}
static ssize_t qeth_l3_dev_sniffer_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
int rc = 0;
unsigned long i;
if (!IS_IQD(card))
return -EPERM;
if (card->options.cq == QETH_CQ_ENABLED)
return -EPERM;
mutex_lock(&card->conf_mutex);
if (card->state != CARD_STATE_DOWN) {
rc = -EPERM;
goto out;
}
rc = kstrtoul(buf, 16, &i);
if (rc) {
rc = -EINVAL;
goto out;
}
switch (i) {
case 0:
card->options.sniffer = i;
break;
case 1:
qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
if (card->ssqd.qdioac2 & CHSC_AC2_SNIFFER_AVAILABLE) {
card->options.sniffer = i;
if (card->qdio.init_pool.buf_count !=
QETH_IN_BUF_COUNT_MAX)
qeth_realloc_buffer_pool(card,
QETH_IN_BUF_COUNT_MAX);
} else
rc = -EPERM;
break;
default:
rc = -EINVAL;
}
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
qeth_l3_dev_sniffer_store);
static ssize_t qeth_l3_dev_hsuid_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
char tmp_hsuid[9];
if (!IS_IQD(card))
return -EPERM;
memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid));
EBCASC(tmp_hsuid, 8);
return sprintf(buf, "%s\n", tmp_hsuid);
}
static ssize_t qeth_l3_dev_hsuid_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
char *tmp;
int rc;
if (!IS_IQD(card))
return -EPERM;
if (card->state != CARD_STATE_DOWN)
return -EPERM;
if (card->options.sniffer)
return -EPERM;
if (card->options.cq == QETH_CQ_NOTAVAILABLE)
return -EPERM;
tmp = strsep((char **)&buf, "\n");
if (strlen(tmp) > 8)
return -EINVAL;
if (card->options.hsuid[0])
/* delete old ip address */
qeth_l3_modify_hsuid(card, false);
if (strlen(tmp) == 0) {
/* delete ip address only */
card->options.hsuid[0] = '\0';
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
qeth_configure_cq(card, QETH_CQ_DISABLED);
return count;
}
if (qeth_configure_cq(card, QETH_CQ_ENABLED))
return -EPERM;
snprintf(card->options.hsuid, sizeof(card->options.hsuid),
"%-8s", tmp);
ASCEBC(card->options.hsuid, 8);
memcpy(card->dev->perm_addr, card->options.hsuid, 9);
rc = qeth_l3_modify_hsuid(card, true);
return rc ? rc : count;
}
static DEVICE_ATTR(hsuid, 0644, qeth_l3_dev_hsuid_show,
qeth_l3_dev_hsuid_store);
static struct attribute *qeth_l3_device_attrs[] = {
&dev_attr_route4.attr,
&dev_attr_route6.attr,
&dev_attr_fake_broadcast.attr,
&dev_attr_sniffer.attr,
&dev_attr_hsuid.attr,
NULL,
};
static const struct attribute_group qeth_l3_device_attr_group = {
.attrs = qeth_l3_device_attrs,
};
static ssize_t qeth_l3_dev_ipato_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return sprintf(buf, "%i\n", card->ipato.enabled? 1:0);
}
static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
bool enable;
int rc = 0;
mutex_lock(&card->conf_mutex);
if (card->state != CARD_STATE_DOWN) {
rc = -EPERM;
goto out;
}
if (sysfs_streq(buf, "toggle")) {
enable = !card->ipato.enabled;
} else if (kstrtobool(buf, &enable)) {
rc = -EINVAL;
goto out;
}
if (card->ipato.enabled != enable) {
card->ipato.enabled = enable;
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static QETH_DEVICE_ATTR(ipato_enable, enable, 0644,
qeth_l3_dev_ipato_enable_show,
qeth_l3_dev_ipato_enable_store);
static ssize_t qeth_l3_dev_ipato_invert4_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return sprintf(buf, "%i\n", card->ipato.invert4? 1:0);
}
static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
bool invert;
int rc = 0;
mutex_lock(&card->conf_mutex);
if (sysfs_streq(buf, "toggle")) {
invert = !card->ipato.invert4;
} else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
goto out;
}
if (card->ipato.invert4 != invert) {
card->ipato.invert4 = invert;
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static QETH_DEVICE_ATTR(ipato_invert4, invert4, 0644,
qeth_l3_dev_ipato_invert4_show,
qeth_l3_dev_ipato_invert4_store);
static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
enum qeth_prot_versions proto)
{
struct qeth_ipato_entry *ipatoe;
char addr_str[40];
int entry_len; /* length of 1 entry string, differs between v4 and v6 */
int i = 0;
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
/* add strlen for "/<mask>\n" */
entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
mutex_lock(&card->ip_lock);
list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
if (ipatoe->proto != proto)
continue;
/* String must not be longer than PAGE_SIZE. So we check if
* string length gets near PAGE_SIZE. Then we can savely display
* the next IPv6 address (worst case, compared to IPv4) */
if ((PAGE_SIZE - i) <= entry_len)
break;
qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str);
i += snprintf(buf + i, PAGE_SIZE - i,
"%s/%i\n", addr_str, ipatoe->mask_bits);
}
mutex_unlock(&card->ip_lock);
i += snprintf(buf + i, PAGE_SIZE - i, "\n");
return i;
}
static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV4);
}
static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto,
u8 *addr, int *mask_bits)
{
const char *start, *end;
char *tmp;
char buffer[40] = {0, };
start = buf;
/* get address string */
end = strchr(start, '/');
if (!end || (end - start >= 40)) {
return -EINVAL;
}
strncpy(buffer, start, end - start);
if (qeth_l3_string_to_ipaddr(buffer, proto, addr)) {
return -EINVAL;
}
start = end + 1;
*mask_bits = simple_strtoul(start, &tmp, 10);
if (!strlen(start) ||
(tmp == start) ||
(*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) {
return -EINVAL;
}
return 0;
}
static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
struct qeth_ipato_entry *ipatoe;
u8 addr[16];
int mask_bits;
int rc = 0;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits);
if (rc)
goto out;
ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL);
if (!ipatoe) {
rc = -ENOMEM;
goto out;
}
ipatoe->proto = proto;
memcpy(ipatoe->addr, addr, (proto == QETH_PROT_IPV4)? 4:16);
ipatoe->mask_bits = mask_bits;
rc = qeth_l3_add_ipato_entry(card, ipatoe);
if (rc)
kfree(ipatoe);
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_ipato_add4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(ipato_add4, add4, 0644,
qeth_l3_dev_ipato_add4_show,
qeth_l3_dev_ipato_add4_store);
static ssize_t qeth_l3_dev_ipato_del_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
u8 addr[16];
int mask_bits;
int rc = 0;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_ipatoe(buf, proto, addr, &mask_bits);
if (!rc)
rc = qeth_l3_del_ipato_entry(card, proto, addr, mask_bits);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_ipato_del4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(ipato_del4, del4, 0200, NULL,
qeth_l3_dev_ipato_del4_store);
static ssize_t qeth_l3_dev_ipato_invert6_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return sprintf(buf, "%i\n", card->ipato.invert6? 1:0);
}
static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
bool invert;
int rc = 0;
mutex_lock(&card->conf_mutex);
if (sysfs_streq(buf, "toggle")) {
invert = !card->ipato.invert6;
} else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
goto out;
}
if (card->ipato.invert6 != invert) {
card->ipato.invert6 = invert;
mutex_lock(&card->ip_lock);
qeth_l3_update_ipato(card);
mutex_unlock(&card->ip_lock);
}
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static QETH_DEVICE_ATTR(ipato_invert6, invert6, 0644,
qeth_l3_dev_ipato_invert6_show,
qeth_l3_dev_ipato_invert6_store);
static ssize_t qeth_l3_dev_ipato_add6_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_add_show(buf, card, QETH_PROT_IPV6);
}
static ssize_t qeth_l3_dev_ipato_add6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_add_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(ipato_add6, add6, 0644,
qeth_l3_dev_ipato_add6_show,
qeth_l3_dev_ipato_add6_store);
static ssize_t qeth_l3_dev_ipato_del6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_ipato_del_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(ipato_del6, del6, 0200, NULL,
qeth_l3_dev_ipato_del6_store);
static struct attribute *qeth_ipato_device_attrs[] = {
&dev_attr_ipato_enable.attr,
&dev_attr_ipato_invert4.attr,
&dev_attr_ipato_add4.attr,
&dev_attr_ipato_del4.attr,
&dev_attr_ipato_invert6.attr,
&dev_attr_ipato_add6.attr,
&dev_attr_ipato_del6.attr,
NULL,
};
static const struct attribute_group qeth_device_ipato_group = {
.name = "ipa_takeover",
.attrs = qeth_ipato_device_attrs,
};
static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf,
enum qeth_prot_versions proto,
enum qeth_ip_types type)
{
struct qeth_card *card = dev_get_drvdata(dev);
struct qeth_ipaddr *ipaddr;
char addr_str[40];
int str_len = 0;
int entry_len; /* length of 1 entry string, differs between v4 and v6 */
int i;
entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
entry_len += 2; /* \n + terminator */
mutex_lock(&card->ip_lock);
hash_for_each(card->ip_htable, i, ipaddr, hnode) {
if (ipaddr->proto != proto || ipaddr->type != type)
continue;
/* String must not be longer than PAGE_SIZE. So we check if
* string length gets near PAGE_SIZE. Then we can savely display
* the next IPv6 address (worst case, compared to IPv4) */
if ((PAGE_SIZE - str_len) <= entry_len)
break;
qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
addr_str);
str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n",
addr_str);
}
mutex_unlock(&card->ip_lock);
str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n");
return str_len;
}
static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV4,
QETH_IP_TYPE_VIPA);
}
static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto,
u8 *addr)
{
if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
return -EINVAL;
}
return 0;
}
static ssize_t qeth_l3_dev_vipa_add_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
u8 addr[16] = {0, };
int rc;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_vipae(buf, proto, addr);
if (!rc)
rc = qeth_l3_modify_rxip_vipa(card, true, addr,
QETH_IP_TYPE_VIPA, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_vipa_add4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(vipa_add4, add4, 0644,
qeth_l3_dev_vipa_add4_show,
qeth_l3_dev_vipa_add4_store);
static ssize_t qeth_l3_dev_vipa_del_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
u8 addr[16];
int rc;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_vipae(buf, proto, addr);
if (!rc)
rc = qeth_l3_modify_rxip_vipa(card, false, addr,
QETH_IP_TYPE_VIPA, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_vipa_del4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(vipa_del4, del4, 0200, NULL,
qeth_l3_dev_vipa_del4_store);
static ssize_t qeth_l3_dev_vipa_add6_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV6,
QETH_IP_TYPE_VIPA);
}
static ssize_t qeth_l3_dev_vipa_add6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_vipa_add_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(vipa_add6, add6, 0644,
qeth_l3_dev_vipa_add6_show,
qeth_l3_dev_vipa_add6_store);
static ssize_t qeth_l3_dev_vipa_del6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_vipa_del_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(vipa_del6, del6, 0200, NULL,
qeth_l3_dev_vipa_del6_store);
static struct attribute *qeth_vipa_device_attrs[] = {
&dev_attr_vipa_add4.attr,
&dev_attr_vipa_del4.attr,
&dev_attr_vipa_add6.attr,
&dev_attr_vipa_del6.attr,
NULL,
};
static const struct attribute_group qeth_device_vipa_group = {
.name = "vipa",
.attrs = qeth_vipa_device_attrs,
};
static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV4,
QETH_IP_TYPE_RXIP);
}
static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto,
u8 *addr)
{
__be32 ipv4_addr;
struct in6_addr ipv6_addr;
if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
return -EINVAL;
}
if (proto == QETH_PROT_IPV4) {
memcpy(&ipv4_addr, addr, sizeof(ipv4_addr));
if (ipv4_is_multicast(ipv4_addr)) {
QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n");
return -EINVAL;
}
} else if (proto == QETH_PROT_IPV6) {
memcpy(&ipv6_addr, addr, sizeof(ipv6_addr));
if (ipv6_addr_is_multicast(&ipv6_addr)) {
QETH_DBF_MESSAGE(2, "multicast rxip not supported.\n");
return -EINVAL;
}
}
return 0;
}
static ssize_t qeth_l3_dev_rxip_add_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
u8 addr[16] = {0, };
int rc;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_rxipe(buf, proto, addr);
if (!rc)
rc = qeth_l3_modify_rxip_vipa(card, true, addr,
QETH_IP_TYPE_RXIP, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_rxip_add4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(rxip_add4, add4, 0644,
qeth_l3_dev_rxip_add4_show,
qeth_l3_dev_rxip_add4_store);
static ssize_t qeth_l3_dev_rxip_del_store(const char *buf, size_t count,
struct qeth_card *card, enum qeth_prot_versions proto)
{
u8 addr[16];
int rc;
mutex_lock(&card->conf_mutex);
rc = qeth_l3_parse_rxipe(buf, proto, addr);
if (!rc)
rc = qeth_l3_modify_rxip_vipa(card, false, addr,
QETH_IP_TYPE_RXIP, proto);
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
static ssize_t qeth_l3_dev_rxip_del4_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV4);
}
static QETH_DEVICE_ATTR(rxip_del4, del4, 0200, NULL,
qeth_l3_dev_rxip_del4_store);
static ssize_t qeth_l3_dev_rxip_add6_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return qeth_l3_dev_ip_add_show(dev, buf, QETH_PROT_IPV6,
QETH_IP_TYPE_RXIP);
}
static ssize_t qeth_l3_dev_rxip_add6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_rxip_add_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(rxip_add6, add6, 0644,
qeth_l3_dev_rxip_add6_show,
qeth_l3_dev_rxip_add6_store);
static ssize_t qeth_l3_dev_rxip_del6_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
return qeth_l3_dev_rxip_del_store(buf, count, card, QETH_PROT_IPV6);
}
static QETH_DEVICE_ATTR(rxip_del6, del6, 0200, NULL,
qeth_l3_dev_rxip_del6_store);
static struct attribute *qeth_rxip_device_attrs[] = {
&dev_attr_rxip_add4.attr,
&dev_attr_rxip_del4.attr,
&dev_attr_rxip_add6.attr,
&dev_attr_rxip_del6.attr,
NULL,
};
static const struct attribute_group qeth_device_rxip_group = {
.name = "rxip",
.attrs = qeth_rxip_device_attrs,
};
static const struct attribute_group *qeth_l3_only_attr_groups[] = {
&qeth_l3_device_attr_group,
&qeth_device_ipato_group,
&qeth_device_vipa_group,
&qeth_device_rxip_group,
NULL,
};
int qeth_l3_create_device_attributes(struct device *dev)
{
return sysfs_create_groups(&dev->kobj, qeth_l3_only_attr_groups);
}
void qeth_l3_remove_device_attributes(struct device *dev)
{
sysfs_remove_groups(&dev->kobj, qeth_l3_only_attr_groups);
}
const struct attribute_group *qeth_l3_attr_groups[] = {
&qeth_device_attr_group,
&qeth_device_blkt_group,
/* l3 specific, see qeth_l3_only_attr_groups: */
&qeth_l3_device_attr_group,
&qeth_device_ipato_group,
&qeth_device_vipa_group,
&qeth_device_rxip_group,
NULL,
};