qeth: HiperSockets Network Traffic Analyzer
New feature to trace HiperSockets network traffic for debugging purposes. Signed-off-by: Ursula Braun <ursula.braun@de.ibm.com> Signed-off-by: Frank Blaschka <frank.blaschka@de.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
ab8932f3e8
commit
76b11f8e27
8 changed files with 336 additions and 86 deletions
|
@ -649,6 +649,7 @@ struct qeth_card_options {
|
|||
int performance_stats;
|
||||
int rx_sg_cb;
|
||||
enum qeth_ipa_isolation_modes isolation;
|
||||
int sniffer;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -737,6 +738,7 @@ struct qeth_card {
|
|||
struct qeth_discipline discipline;
|
||||
atomic_t force_alloc_skb;
|
||||
struct service_level qeth_service_level;
|
||||
struct qdio_ssqd_desc ssqd;
|
||||
};
|
||||
|
||||
struct qeth_card_list_struct {
|
||||
|
@ -811,7 +813,8 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
|
|||
struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *,
|
||||
enum qeth_ipa_cmds, enum qeth_prot_versions);
|
||||
int qeth_query_setadapterparms(struct qeth_card *);
|
||||
int qeth_check_qdio_errors(struct qdio_buffer *, unsigned int, const char *);
|
||||
int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *,
|
||||
unsigned int, const char *);
|
||||
void qeth_queue_input_buffer(struct qeth_card *, int);
|
||||
struct sk_buff *qeth_core_get_next_skb(struct qeth_card *,
|
||||
struct qdio_buffer *, struct qdio_buffer_element **, int *,
|
||||
|
|
|
@ -269,6 +269,7 @@ int qeth_realloc_buffer_pool(struct qeth_card *card, int bufcnt)
|
|||
card->qdio.init_pool.buf_count = bufcnt;
|
||||
return qeth_alloc_buffer_pool(card);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_realloc_buffer_pool);
|
||||
|
||||
static int qeth_issue_next_read(struct qeth_card *card)
|
||||
{
|
||||
|
@ -350,8 +351,10 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
|
|||
if (IS_IPA(iob->data)) {
|
||||
cmd = (struct qeth_ipa_cmd *) PDU_ENCAPSULATION(iob->data);
|
||||
if (IS_IPA_REPLY(cmd)) {
|
||||
if (cmd->hdr.command < IPA_CMD_SETCCID ||
|
||||
cmd->hdr.command > IPA_CMD_MODCCID)
|
||||
if (cmd->hdr.command != IPA_CMD_SETCCID &&
|
||||
cmd->hdr.command != IPA_CMD_DELCCID &&
|
||||
cmd->hdr.command != IPA_CMD_MODCCID &&
|
||||
cmd->hdr.command != IPA_CMD_SET_DIAG_ASS)
|
||||
qeth_issue_ipa_msg(cmd,
|
||||
cmd->hdr.return_code, card);
|
||||
return cmd;
|
||||
|
@ -1100,11 +1103,6 @@ static int qeth_setup_card(struct qeth_card *card)
|
|||
card->thread_running_mask = 0;
|
||||
INIT_WORK(&card->kernel_thread_starter, qeth_start_kernel_thread);
|
||||
INIT_LIST_HEAD(&card->ip_list);
|
||||
card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
|
||||
if (!card->ip_tbd_list) {
|
||||
QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(card->ip_tbd_list);
|
||||
INIT_LIST_HEAD(&card->cmd_waiter_list);
|
||||
init_waitqueue_head(&card->wait_q);
|
||||
|
@ -1138,21 +1136,30 @@ static struct qeth_card *qeth_alloc_card(void)
|
|||
QETH_DBF_TEXT(SETUP, 2, "alloccrd");
|
||||
card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
|
||||
if (!card)
|
||||
return NULL;
|
||||
goto out;
|
||||
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
|
||||
if (qeth_setup_channel(&card->read)) {
|
||||
kfree(card);
|
||||
return NULL;
|
||||
}
|
||||
if (qeth_setup_channel(&card->write)) {
|
||||
qeth_clean_channel(&card->read);
|
||||
kfree(card);
|
||||
return NULL;
|
||||
card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
|
||||
if (!card->ip_tbd_list) {
|
||||
QETH_DBF_TEXT(SETUP, 0, "iptbdnom");
|
||||
goto out_card;
|
||||
}
|
||||
if (qeth_setup_channel(&card->read))
|
||||
goto out_ip;
|
||||
if (qeth_setup_channel(&card->write))
|
||||
goto out_channel;
|
||||
card->options.layer2 = -1;
|
||||
card->qeth_service_level.seq_print = qeth_core_sl_print;
|
||||
register_service_level(&card->qeth_service_level);
|
||||
return card;
|
||||
|
||||
out_channel:
|
||||
qeth_clean_channel(&card->read);
|
||||
out_ip:
|
||||
kfree(card->ip_tbd_list);
|
||||
out_card:
|
||||
kfree(card);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int qeth_determine_card_type(struct qeth_card *card)
|
||||
|
@ -2573,8 +2580,8 @@ int qeth_query_setadapterparms(struct qeth_card *card)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(qeth_query_setadapterparms);
|
||||
|
||||
int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
|
||||
const char *dbftext)
|
||||
int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf,
|
||||
unsigned int qdio_error, const char *dbftext)
|
||||
{
|
||||
if (qdio_error) {
|
||||
QETH_DBF_TEXT(TRACE, 2, dbftext);
|
||||
|
@ -2584,7 +2591,11 @@ int qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
|
|||
QETH_DBF_TEXT_(QERR, 2, " F14=%02X",
|
||||
buf->element[14].flags & 0xff);
|
||||
QETH_DBF_TEXT_(QERR, 2, " qerr=%X", qdio_error);
|
||||
return 1;
|
||||
if ((buf->element[15].flags & 0xff) == 0x12) {
|
||||
card->stats.rx_dropped++;
|
||||
return 0;
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -2667,7 +2678,7 @@ static int qeth_handle_send_error(struct qeth_card *card,
|
|||
qdio_err = 1;
|
||||
}
|
||||
}
|
||||
qeth_check_qdio_errors(buffer->buffer, qdio_err, "qouterr");
|
||||
qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr");
|
||||
|
||||
if (!qdio_err)
|
||||
return QETH_SEND_ERROR_NONE;
|
||||
|
@ -3509,6 +3520,7 @@ void qeth_tx_timeout(struct net_device *dev)
|
|||
{
|
||||
struct qeth_card *card;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 4, "txtimeo");
|
||||
card = dev->ml_priv;
|
||||
card->stats.tx_errors++;
|
||||
qeth_schedule_recovery(card);
|
||||
|
@ -3847,9 +3859,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
|
|||
|
||||
int qeth_core_hardsetup_card(struct qeth_card *card)
|
||||
{
|
||||
struct qdio_ssqd_desc *ssqd;
|
||||
int retries = 0;
|
||||
int mpno = 0;
|
||||
int rc;
|
||||
|
||||
QETH_DBF_TEXT(SETUP, 2, "hrdsetup");
|
||||
|
@ -3882,31 +3892,6 @@ retriable:
|
|||
else
|
||||
goto retry;
|
||||
}
|
||||
|
||||
rc = qeth_get_unitaddr(card);
|
||||
if (rc) {
|
||||
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL);
|
||||
if (!ssqd) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd);
|
||||
if (rc == 0)
|
||||
mpno = ssqd->pcnt;
|
||||
kfree(ssqd);
|
||||
|
||||
if (mpno)
|
||||
mpno = min(mpno - 1, QETH_MAX_PORTNO);
|
||||
if (card->info.portno > mpno) {
|
||||
QETH_DBF_MESSAGE(2, "Device %s does not offer port number %d"
|
||||
"\n.", CARD_BUS_ID(card), card->info.portno);
|
||||
rc = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
qeth_init_tokens(card);
|
||||
qeth_init_func_level(card);
|
||||
rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
|
||||
|
@ -3990,7 +3975,7 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
|
|||
struct qdio_buffer_element *element = *__element;
|
||||
int offset = *__offset;
|
||||
struct sk_buff *skb = NULL;
|
||||
int skb_len;
|
||||
int skb_len = 0;
|
||||
void *data_ptr;
|
||||
int data_len;
|
||||
int headroom = 0;
|
||||
|
@ -4009,20 +3994,24 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
|
|||
*hdr = element->addr + offset;
|
||||
|
||||
offset += sizeof(struct qeth_hdr);
|
||||
if (card->options.layer2) {
|
||||
if (card->info.type == QETH_CARD_TYPE_OSN) {
|
||||
skb_len = (*hdr)->hdr.osn.pdu_length;
|
||||
headroom = sizeof(struct qeth_hdr);
|
||||
} else {
|
||||
skb_len = (*hdr)->hdr.l2.pkt_length;
|
||||
}
|
||||
} else {
|
||||
switch ((*hdr)->hdr.l2.id) {
|
||||
case QETH_HEADER_TYPE_LAYER2:
|
||||
skb_len = (*hdr)->hdr.l2.pkt_length;
|
||||
break;
|
||||
case QETH_HEADER_TYPE_LAYER3:
|
||||
skb_len = (*hdr)->hdr.l3.length;
|
||||
if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) ||
|
||||
(card->info.link_type == QETH_LINK_TYPE_HSTR))
|
||||
headroom = TR_HLEN;
|
||||
else
|
||||
headroom = ETH_HLEN;
|
||||
break;
|
||||
case QETH_HEADER_TYPE_OSN:
|
||||
skb_len = (*hdr)->hdr.osn.pdu_length;
|
||||
headroom = sizeof(struct qeth_hdr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!skb_len)
|
||||
|
@ -4177,6 +4166,33 @@ void qeth_core_free_discipline(struct qeth_card *card)
|
|||
card->discipline.ccwgdriver = NULL;
|
||||
}
|
||||
|
||||
static void qeth_determine_capabilities(struct qeth_card *card)
|
||||
{
|
||||
int rc;
|
||||
|
||||
QETH_DBF_TEXT(SETUP, 2, "detcapab");
|
||||
rc = ccw_device_set_online(CARD_DDEV(card));
|
||||
if (rc) {
|
||||
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = qeth_get_unitaddr(card);
|
||||
if (rc) {
|
||||
QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
|
||||
goto out_offline;
|
||||
}
|
||||
|
||||
rc = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
|
||||
|
||||
out_offline:
|
||||
ccw_device_set_offline(CARD_DDEV(card));
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
||||
{
|
||||
struct qeth_card *card;
|
||||
|
@ -4242,6 +4258,8 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
|
|||
write_lock_irqsave(&qeth_core_card_list.rwlock, flags);
|
||||
list_add_tail(&card->list, &qeth_core_card_list.list);
|
||||
write_unlock_irqrestore(&qeth_core_card_list.rwlock, flags);
|
||||
|
||||
qeth_determine_capabilities(card);
|
||||
return 0;
|
||||
|
||||
err_card:
|
||||
|
|
|
@ -156,6 +156,8 @@ enum qeth_ipa_return_codes {
|
|||
IPA_RC_IP_TABLE_FULL = 0x0002,
|
||||
IPA_RC_UNKNOWN_ERROR = 0x0003,
|
||||
IPA_RC_UNSUPPORTED_COMMAND = 0x0004,
|
||||
IPA_RC_TRACE_ALREADY_ACTIVE = 0x0005,
|
||||
IPA_RC_INVALID_FORMAT = 0x0006,
|
||||
IPA_RC_DUP_IPV6_REMOTE = 0x0008,
|
||||
IPA_RC_DUP_IPV6_HOME = 0x0010,
|
||||
IPA_RC_UNREGISTERED_ADDR = 0x0011,
|
||||
|
@ -196,6 +198,11 @@ enum qeth_ipa_return_codes {
|
|||
IPA_RC_INVALID_IP_VERSION2 = 0xf001,
|
||||
IPA_RC_FFFF = 0xffff
|
||||
};
|
||||
/* for DELIP */
|
||||
#define IPA_RC_IP_ADDRESS_NOT_DEFINED IPA_RC_PRIMARY_ALREADY_DEFINED
|
||||
/* for SET_DIAGNOSTIC_ASSIST */
|
||||
#define IPA_RC_INVALID_SUBCMD IPA_RC_IP_TABLE_FULL
|
||||
#define IPA_RC_HARDWARE_AUTH_ERROR IPA_RC_UNKNOWN_ERROR
|
||||
|
||||
/* IPA function flags; each flag marks availability of respective function */
|
||||
enum qeth_ipa_funcs {
|
||||
|
@ -246,6 +253,7 @@ enum qeth_ipa_setadp_cmd {
|
|||
IPA_SETADP_SET_SNMP_CONTROL = 0x00000200L,
|
||||
IPA_SETADP_QUERY_CARD_INFO = 0x00000400L,
|
||||
IPA_SETADP_SET_PROMISC_MODE = 0x00000800L,
|
||||
IPA_SETADP_SET_DIAG_ASSIST = 0x00002000L,
|
||||
IPA_SETADP_SET_ACCESS_CONTROL = 0x00010000L,
|
||||
};
|
||||
enum qeth_ipa_mac_ops {
|
||||
|
@ -424,6 +432,40 @@ struct qeth_create_destroy_address {
|
|||
__u8 unique_id[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* SET DIAGNOSTIC ASSIST IPA Command: *************************************/
|
||||
|
||||
enum qeth_diags_cmds {
|
||||
QETH_DIAGS_CMD_QUERY = 0x0001,
|
||||
QETH_DIAGS_CMD_TRAP = 0x0002,
|
||||
QETH_DIAGS_CMD_TRACE = 0x0004,
|
||||
QETH_DIAGS_CMD_NOLOG = 0x0008,
|
||||
QETH_DIAGS_CMD_DUMP = 0x0010,
|
||||
};
|
||||
|
||||
enum qeth_diags_trace_types {
|
||||
QETH_DIAGS_TYPE_HIPERSOCKET = 0x02,
|
||||
};
|
||||
|
||||
enum qeth_diags_trace_cmds {
|
||||
QETH_DIAGS_CMD_TRACE_ENABLE = 0x0001,
|
||||
QETH_DIAGS_CMD_TRACE_DISABLE = 0x0002,
|
||||
QETH_DIAGS_CMD_TRACE_MODIFY = 0x0004,
|
||||
QETH_DIAGS_CMD_TRACE_REPLACE = 0x0008,
|
||||
QETH_DIAGS_CMD_TRACE_QUERY = 0x0010,
|
||||
};
|
||||
|
||||
struct qeth_ipacmd_diagass {
|
||||
__u32 host_tod2;
|
||||
__u32:32;
|
||||
__u16 subcmd_len;
|
||||
__u16:16;
|
||||
__u32 subcmd;
|
||||
__u8 type;
|
||||
__u8 action;
|
||||
__u16 options;
|
||||
__u32:32;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Header for each IPA command */
|
||||
struct qeth_ipacmd_hdr {
|
||||
__u8 command;
|
||||
|
@ -452,6 +494,7 @@ struct qeth_ipa_cmd {
|
|||
struct qeth_create_destroy_address create_destroy_addr;
|
||||
struct qeth_ipacmd_setadpparms setadapterparms;
|
||||
struct qeth_set_routing setrtg;
|
||||
struct qeth_ipacmd_diagass diagass;
|
||||
} data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
@ -469,7 +512,6 @@ enum qeth_ipa_arp_return_codes {
|
|||
QETH_IPA_ARP_RC_Q_NO_DATA = 0x0008,
|
||||
};
|
||||
|
||||
|
||||
extern char *qeth_get_ipa_msg(enum qeth_ipa_return_codes rc);
|
||||
extern char *qeth_get_ipa_cmd_name(enum qeth_ipa_cmds cmd);
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
|
|||
{
|
||||
struct qeth_card *card = dev_get_drvdata(dev);
|
||||
char *tmp;
|
||||
unsigned int portno;
|
||||
unsigned int portno, limit;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
@ -128,9 +128,11 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
|
|||
return -EPERM;
|
||||
|
||||
portno = simple_strtoul(buf, &tmp, 16);
|
||||
if (portno > QETH_MAX_PORTNO) {
|
||||
if (portno > QETH_MAX_PORTNO)
|
||||
return -EINVAL;
|
||||
limit = (card->ssqd.pcnt ? card->ssqd.pcnt - 1 : card->ssqd.pcnt);
|
||||
if (portno > limit)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
card->info.portno = portno;
|
||||
return count;
|
||||
|
|
|
@ -769,7 +769,8 @@ static void qeth_l2_qdio_input_handler(struct ccw_device *ccwdev,
|
|||
index = i % QDIO_MAX_BUFFERS_PER_Q;
|
||||
buffer = &card->qdio.in_q->bufs[index];
|
||||
if (!(qdio_err &&
|
||||
qeth_check_qdio_errors(buffer->buffer, qdio_err, "qinerr")))
|
||||
qeth_check_qdio_errors(card, buffer->buffer, qdio_err,
|
||||
"qinerr")))
|
||||
qeth_l2_process_inbound_buffer(card, buffer, index);
|
||||
/* clear buffer and give back to hardware */
|
||||
qeth_put_buffer_pool_entry(card, buffer->pool_entry);
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "qeth_core.h"
|
||||
|
||||
#define QETH_SNIFF_AVAIL 0x0008
|
||||
|
||||
struct qeth_ipaddr {
|
||||
struct list_head entry;
|
||||
enum qeth_ip_types type;
|
||||
|
|
|
@ -242,6 +242,8 @@ static int __qeth_l3_insert_ip_todo(struct qeth_card *card,
|
|||
struct qeth_ipaddr *tmp, *t;
|
||||
int found = 0;
|
||||
|
||||
if (card->options.sniffer)
|
||||
return 0;
|
||||
list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
|
||||
if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
|
||||
(tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
|
||||
|
@ -457,6 +459,8 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
|
|||
QETH_DBF_TEXT(TRACE, 2, "sdiplist");
|
||||
QETH_DBF_HEX(TRACE, 2, &card, sizeof(void *));
|
||||
|
||||
if (card->options.sniffer)
|
||||
return;
|
||||
spin_lock_irqsave(&card->ip_lock, flags);
|
||||
tbd_list = card->ip_tbd_list;
|
||||
card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
|
||||
|
@ -495,7 +499,7 @@ static void qeth_l3_set_ip_addr_list(struct qeth_card *card)
|
|||
spin_unlock_irqrestore(&card->ip_lock, flags);
|
||||
rc = qeth_l3_deregister_addr_entry(card, addr);
|
||||
spin_lock_irqsave(&card->ip_lock, flags);
|
||||
if (!rc || (rc == IPA_RC_PRIMARY_ALREADY_DEFINED))
|
||||
if (!rc || (rc == IPA_RC_IP_ADDRESS_NOT_DEFINED))
|
||||
kfree(addr);
|
||||
else
|
||||
list_add_tail(&addr->entry, &card->ip_list);
|
||||
|
@ -513,6 +517,8 @@ static void qeth_l3_clear_ip_list(struct qeth_card *card, int clean,
|
|||
unsigned long flags;
|
||||
|
||||
QETH_DBF_TEXT(TRACE, 4, "clearip");
|
||||
if (recover && card->options.sniffer)
|
||||
return;
|
||||
spin_lock_irqsave(&card->ip_lock, flags);
|
||||
/* clear todo list */
|
||||
list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry) {
|
||||
|
@ -1674,6 +1680,76 @@ static int qeth_l3_get_unique_id(struct qeth_card *card)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
qeth_diags_trace_cb(struct qeth_card *card, struct qeth_reply *reply,
|
||||
unsigned long data)
|
||||
{
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
__u16 rc;
|
||||
|
||||
QETH_DBF_TEXT(SETUP, 2, "diastrcb");
|
||||
|
||||
cmd = (struct qeth_ipa_cmd *)data;
|
||||
rc = cmd->hdr.return_code;
|
||||
if (rc) {
|
||||
QETH_DBF_TEXT_(TRACE, 2, "dxter%x", rc);
|
||||
if (cmd->data.diagass.action == QETH_DIAGS_CMD_TRACE_ENABLE) {
|
||||
switch (rc) {
|
||||
case IPA_RC_HARDWARE_AUTH_ERROR:
|
||||
dev_warn(&card->gdev->dev, "The device is not "
|
||||
"authorized to run as a HiperSockets "
|
||||
"network traffic analyzer\n");
|
||||
break;
|
||||
case IPA_RC_TRACE_ALREADY_ACTIVE:
|
||||
dev_warn(&card->gdev->dev, "A HiperSockets "
|
||||
"network traffic analyzer is already "
|
||||
"active in the HiperSockets LAN\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd->data.diagass.action) {
|
||||
case QETH_DIAGS_CMD_TRACE_QUERY:
|
||||
break;
|
||||
case QETH_DIAGS_CMD_TRACE_DISABLE:
|
||||
card->info.promisc_mode = SET_PROMISC_MODE_OFF;
|
||||
dev_info(&card->gdev->dev, "The HiperSockets network traffic "
|
||||
"analyzer is deactivated\n");
|
||||
break;
|
||||
case QETH_DIAGS_CMD_TRACE_ENABLE:
|
||||
card->info.promisc_mode = SET_PROMISC_MODE_ON;
|
||||
dev_info(&card->gdev->dev, "The HiperSockets network traffic "
|
||||
"analyzer is activated\n");
|
||||
break;
|
||||
default:
|
||||
QETH_DBF_MESSAGE(2, "Unknown sniffer action (0x%04x) on %s\n",
|
||||
cmd->data.diagass.action, QETH_CARD_IFNAME(card));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd)
|
||||
{
|
||||
struct qeth_cmd_buffer *iob;
|
||||
struct qeth_ipa_cmd *cmd;
|
||||
|
||||
QETH_DBF_TEXT(SETUP, 2, "diagtrac");
|
||||
|
||||
iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0);
|
||||
cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
|
||||
cmd->data.diagass.subcmd_len = 16;
|
||||
cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE;
|
||||
cmd->data.diagass.type = QETH_DIAGS_TYPE_HIPERSOCKET;
|
||||
cmd->data.diagass.action = diags_cmd;
|
||||
return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL);
|
||||
}
|
||||
|
||||
static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac,
|
||||
struct net_device *dev)
|
||||
{
|
||||
|
@ -1951,7 +2027,10 @@ static inline __u16 qeth_l3_rebuild_skb(struct qeth_card *card,
|
|||
case QETH_CAST_ANYCAST:
|
||||
case QETH_CAST_NOCAST:
|
||||
default:
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
if (card->options.sniffer)
|
||||
skb->pkt_type = PACKET_OTHERHOST;
|
||||
else
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
memcpy(tg_addr, card->dev->dev_addr,
|
||||
card->dev->addr_len);
|
||||
}
|
||||
|
@ -2007,7 +2086,6 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
|
|||
int offset;
|
||||
__u16 vlan_tag = 0;
|
||||
unsigned int len;
|
||||
|
||||
/* get first element of current buffer */
|
||||
element = (struct qdio_buffer_element *)&buf->buffer->element[0];
|
||||
offset = 0;
|
||||
|
@ -2026,7 +2104,7 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
|
|||
case QETH_HEADER_TYPE_LAYER3:
|
||||
vlan_tag = qeth_l3_rebuild_skb(card, skb, hdr);
|
||||
len = skb->len;
|
||||
if (vlan_tag)
|
||||
if (vlan_tag && !card->options.sniffer)
|
||||
if (card->vlangrp)
|
||||
vlan_hwaccel_rx(skb, card->vlangrp,
|
||||
vlan_tag);
|
||||
|
@ -2037,6 +2115,16 @@ static void qeth_l3_process_inbound_buffer(struct qeth_card *card,
|
|||
else
|
||||
netif_rx(skb);
|
||||
break;
|
||||
case QETH_HEADER_TYPE_LAYER2: /* for HiperSockets sniffer */
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||
if (card->options.checksum_type == NO_CHECKSUMMING)
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
else
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
len = skb->len;
|
||||
netif_receive_skb(skb);
|
||||
break;
|
||||
default:
|
||||
dev_kfree_skb_any(skb);
|
||||
QETH_DBF_TEXT(TRACE, 3, "inbunkno");
|
||||
|
@ -2118,6 +2206,9 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
|
|||
QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *));
|
||||
|
||||
qeth_set_allowed_threads(card, 0, 1);
|
||||
if (card->options.sniffer &&
|
||||
(card->info.promisc_mode == SET_PROMISC_MODE_ON))
|
||||
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
||||
if (card->read.state == CH_STATE_UP &&
|
||||
card->write.state == CH_STATE_UP &&
|
||||
(card->state == CARD_STATE_UP)) {
|
||||
|
@ -2162,6 +2253,36 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* test for and Switch promiscuous mode (on or off)
|
||||
* either for guestlan or HiperSocket Sniffer
|
||||
*/
|
||||
static void
|
||||
qeth_l3_handle_promisc_mode(struct qeth_card *card)
|
||||
{
|
||||
struct net_device *dev = card->dev;
|
||||
|
||||
if (((dev->flags & IFF_PROMISC) &&
|
||||
(card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
|
||||
(!(dev->flags & IFF_PROMISC) &&
|
||||
(card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
|
||||
return;
|
||||
|
||||
if (card->info.guestlan) { /* Guestlan trace */
|
||||
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
||||
qeth_setadp_promisc_mode(card);
|
||||
} else if (card->options.sniffer && /* HiperSockets trace */
|
||||
qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
|
||||
if (dev->flags & IFF_PROMISC) {
|
||||
QETH_DBF_TEXT(TRACE, 3, "+promisc");
|
||||
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_ENABLE);
|
||||
} else {
|
||||
QETH_DBF_TEXT(TRACE, 3, "-promisc");
|
||||
qeth_diags_trace(card, QETH_DIAGS_CMD_TRACE_DISABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void qeth_l3_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
struct qeth_card *card = dev->ml_priv;
|
||||
|
@ -2170,15 +2291,17 @@ static void qeth_l3_set_multicast_list(struct net_device *dev)
|
|||
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
|
||||
(card->state != CARD_STATE_UP))
|
||||
return;
|
||||
qeth_l3_delete_mc_addresses(card);
|
||||
qeth_l3_add_multicast_ipv4(card);
|
||||
if (!card->options.sniffer) {
|
||||
qeth_l3_delete_mc_addresses(card);
|
||||
qeth_l3_add_multicast_ipv4(card);
|
||||
#ifdef CONFIG_QETH_IPV6
|
||||
qeth_l3_add_multicast_ipv6(card);
|
||||
qeth_l3_add_multicast_ipv6(card);
|
||||
#endif
|
||||
qeth_l3_set_ip_addr_list(card);
|
||||
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
||||
return;
|
||||
qeth_setadp_promisc_mode(card);
|
||||
qeth_l3_set_ip_addr_list(card);
|
||||
if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
|
||||
return;
|
||||
}
|
||||
qeth_l3_handle_promisc_mode(card);
|
||||
}
|
||||
|
||||
static const char *qeth_l3_arp_get_error_cause(int *rc)
|
||||
|
@ -2778,8 +2901,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
int nr_frags;
|
||||
|
||||
if ((card->info.type == QETH_CARD_TYPE_IQD) &&
|
||||
(skb->protocol != htons(ETH_P_IPV6)) &&
|
||||
(skb->protocol != htons(ETH_P_IP)))
|
||||
(((skb->protocol != htons(ETH_P_IPV6)) &&
|
||||
(skb->protocol != htons(ETH_P_IP))) ||
|
||||
card->options.sniffer))
|
||||
goto tx_drop;
|
||||
|
||||
if ((card->state != CARD_STATE_UP) || !card->lan_online) {
|
||||
|
@ -3155,7 +3279,7 @@ static void qeth_l3_qdio_input_handler(struct ccw_device *ccwdev,
|
|||
index = i % QDIO_MAX_BUFFERS_PER_Q;
|
||||
buffer = &card->qdio.in_q->bufs[index];
|
||||
if (!(qdio_err &&
|
||||
qeth_check_qdio_errors(buffer->buffer,
|
||||
qeth_check_qdio_errors(card, buffer->buffer,
|
||||
qdio_err, "qinerr")))
|
||||
qeth_l3_process_inbound_buffer(card, buffer, index);
|
||||
/* clear buffer and give back to hardware */
|
||||
|
@ -3250,20 +3374,22 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
|
|||
goto out_remove;
|
||||
} else
|
||||
card->lan_online = 1;
|
||||
qeth_l3_set_large_send(card, card->options.large_send);
|
||||
|
||||
rc = qeth_l3_setadapter_parms(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
|
||||
rc = qeth_l3_start_ipassists(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
||||
rc = qeth_l3_setrouting_v4(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
|
||||
rc = qeth_l3_setrouting_v6(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
|
||||
if (!card->options.sniffer) {
|
||||
rc = qeth_l3_start_ipassists(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "3err%d", rc);
|
||||
qeth_l3_set_large_send(card, card->options.large_send);
|
||||
rc = qeth_l3_setrouting_v4(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc);
|
||||
rc = qeth_l3_setrouting_v6(card);
|
||||
if (rc)
|
||||
QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc);
|
||||
}
|
||||
netif_tx_disable(card->dev);
|
||||
|
||||
rc = qeth_init_qdio_queues(card);
|
||||
|
|
|
@ -319,6 +319,61 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev,
|
|||
static DEVICE_ATTR(checksumming, 0644, qeth_l3_dev_checksum_show,
|
||||
qeth_l3_dev_checksum_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);
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
||||
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 ret;
|
||||
unsigned long i;
|
||||
|
||||
if (!card)
|
||||
return -EINVAL;
|
||||
|
||||
if (card->info.type != QETH_CARD_TYPE_IQD)
|
||||
return -EPERM;
|
||||
|
||||
if ((card->state != CARD_STATE_DOWN) &&
|
||||
(card->state != CARD_STATE_RECOVER))
|
||||
return -EPERM;
|
||||
|
||||
ret = strict_strtoul(buf, 16, &i);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
switch (i) {
|
||||
case 0:
|
||||
card->options.sniffer = i;
|
||||
break;
|
||||
case 1:
|
||||
ret = qdio_get_ssqd_desc(CARD_DDEV(card), &card->ssqd);
|
||||
if (card->ssqd.qdioac2 & QETH_SNIFF_AVAIL) {
|
||||
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);
|
||||
break;
|
||||
} else
|
||||
return -EPERM;
|
||||
default: /* fall through */
|
||||
return -EINVAL;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(sniffer, 0644, qeth_l3_dev_sniffer_show,
|
||||
qeth_l3_dev_sniffer_store);
|
||||
|
||||
static ssize_t qeth_l3_dev_large_send_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -373,6 +428,7 @@ static struct attribute *qeth_l3_device_attrs[] = {
|
|||
&dev_attr_broadcast_mode.attr,
|
||||
&dev_attr_canonical_macaddr.attr,
|
||||
&dev_attr_checksumming.attr,
|
||||
&dev_attr_sniffer.attr,
|
||||
&dev_attr_large_send.attr,
|
||||
NULL,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue