mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-27 17:11:46 +00:00
netiucv: allow multiple interfaces to same peer
The NETIUCV device driver allows to connect a Linux guest on z/VM to another z/VM guest based on the z/VM communication facility IUCV. Multiple output paths to different guests are possible, as well as multiple input paths from different guests. With this feature, you can configure multiple point-to-point NETIUCV interfaces between your Linux on System z instance and another z/VM guest. 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
f78ac2bbb1
commit
08e3356cc2
1 changed files with 142 additions and 89 deletions
|
@ -63,6 +63,7 @@
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
#include <asm/ebcdic.h>
|
||||||
|
|
||||||
#include <net/iucv/iucv.h>
|
#include <net/iucv/iucv.h>
|
||||||
#include "fsm.h"
|
#include "fsm.h"
|
||||||
|
@ -75,7 +76,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver");
|
||||||
* Debug Facility stuff
|
* Debug Facility stuff
|
||||||
*/
|
*/
|
||||||
#define IUCV_DBF_SETUP_NAME "iucv_setup"
|
#define IUCV_DBF_SETUP_NAME "iucv_setup"
|
||||||
#define IUCV_DBF_SETUP_LEN 32
|
#define IUCV_DBF_SETUP_LEN 64
|
||||||
#define IUCV_DBF_SETUP_PAGES 2
|
#define IUCV_DBF_SETUP_PAGES 2
|
||||||
#define IUCV_DBF_SETUP_NR_AREAS 1
|
#define IUCV_DBF_SETUP_NR_AREAS 1
|
||||||
#define IUCV_DBF_SETUP_LEVEL 3
|
#define IUCV_DBF_SETUP_LEVEL 3
|
||||||
|
@ -226,6 +227,7 @@ struct iucv_connection {
|
||||||
struct net_device *netdev;
|
struct net_device *netdev;
|
||||||
struct connection_profile prof;
|
struct connection_profile prof;
|
||||||
char userid[9];
|
char userid[9];
|
||||||
|
char userdata[17];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,7 +265,7 @@ struct ll_header {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NETIUCV_HDRLEN (sizeof(struct ll_header))
|
#define NETIUCV_HDRLEN (sizeof(struct ll_header))
|
||||||
#define NETIUCV_BUFSIZE_MAX 32768
|
#define NETIUCV_BUFSIZE_MAX 65537
|
||||||
#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
|
#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX
|
||||||
#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
|
#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN)
|
||||||
#define NETIUCV_MTU_DEFAULT 9216
|
#define NETIUCV_MTU_DEFAULT 9216
|
||||||
|
@ -288,7 +290,12 @@ static inline int netiucv_test_and_set_busy(struct net_device *dev)
|
||||||
return test_and_set_bit(0, &priv->tbusy);
|
return test_and_set_bit(0, &priv->tbusy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 iucvMagic[16] = {
|
static u8 iucvMagic_ascii[16] = {
|
||||||
|
0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||||
|
0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
|
||||||
|
};
|
||||||
|
|
||||||
|
static u8 iucvMagic_ebcdic[16] = {
|
||||||
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
|
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
|
||||||
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
|
0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
|
||||||
};
|
};
|
||||||
|
@ -301,18 +308,38 @@ static u8 iucvMagic[16] = {
|
||||||
*
|
*
|
||||||
* @returns The printable string (static data!!)
|
* @returns The printable string (static data!!)
|
||||||
*/
|
*/
|
||||||
static char *netiucv_printname(char *name)
|
static char *netiucv_printname(char *name, int len)
|
||||||
{
|
{
|
||||||
static char tmp[9];
|
static char tmp[17];
|
||||||
char *p = tmp;
|
char *p = tmp;
|
||||||
memcpy(tmp, name, 8);
|
memcpy(tmp, name, len);
|
||||||
tmp[8] = '\0';
|
tmp[len] = '\0';
|
||||||
while (*p && (!isspace(*p)))
|
while (*p && ((p - tmp) < len) && (!isspace(*p)))
|
||||||
p++;
|
p++;
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *netiucv_printuser(struct iucv_connection *conn)
|
||||||
|
{
|
||||||
|
static char tmp_uid[9];
|
||||||
|
static char tmp_udat[17];
|
||||||
|
static char buf[100];
|
||||||
|
|
||||||
|
if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) {
|
||||||
|
tmp_uid[8] = '\0';
|
||||||
|
tmp_udat[16] = '\0';
|
||||||
|
memcpy(tmp_uid, conn->userid, 8);
|
||||||
|
memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8);
|
||||||
|
memcpy(tmp_udat, conn->userdata, 16);
|
||||||
|
EBCASC(tmp_udat, 16);
|
||||||
|
memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16);
|
||||||
|
sprintf(buf, "%s.%s", tmp_uid, tmp_udat);
|
||||||
|
return buf;
|
||||||
|
} else
|
||||||
|
return netiucv_printname(conn->userid, 8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* States of the interface statemachine.
|
* States of the interface statemachine.
|
||||||
*/
|
*/
|
||||||
|
@ -563,15 +590,18 @@ static int netiucv_callback_connreq(struct iucv_path *path,
|
||||||
{
|
{
|
||||||
struct iucv_connection *conn = path->private;
|
struct iucv_connection *conn = path->private;
|
||||||
struct iucv_event ev;
|
struct iucv_event ev;
|
||||||
|
static char tmp_user[9];
|
||||||
|
static char tmp_udat[17];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (memcmp(iucvMagic, ipuser, 16))
|
|
||||||
/* ipuser must match iucvMagic. */
|
|
||||||
return -EINVAL;
|
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
|
memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8);
|
||||||
|
memcpy(tmp_udat, ipuser, 16);
|
||||||
|
EBCASC(tmp_udat, 16);
|
||||||
read_lock_bh(&iucv_connection_rwlock);
|
read_lock_bh(&iucv_connection_rwlock);
|
||||||
list_for_each_entry(conn, &iucv_connection_list, list) {
|
list_for_each_entry(conn, &iucv_connection_list, list) {
|
||||||
if (strncmp(ipvmid, conn->userid, 8))
|
if (strncmp(ipvmid, conn->userid, 8) ||
|
||||||
|
strncmp(ipuser, conn->userdata, 16))
|
||||||
continue;
|
continue;
|
||||||
/* Found a matching connection for this path. */
|
/* Found a matching connection for this path. */
|
||||||
conn->path = path;
|
conn->path = path;
|
||||||
|
@ -580,6 +610,8 @@ static int netiucv_callback_connreq(struct iucv_path *path,
|
||||||
fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
|
fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev);
|
||||||
rc = 0;
|
rc = 0;
|
||||||
}
|
}
|
||||||
|
IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n",
|
||||||
|
tmp_user, netiucv_printname(tmp_udat, 16));
|
||||||
read_unlock_bh(&iucv_connection_rwlock);
|
read_unlock_bh(&iucv_connection_rwlock);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -816,7 +848,7 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg)
|
||||||
conn->path = path;
|
conn->path = path;
|
||||||
path->msglim = NETIUCV_QUEUELEN_DEFAULT;
|
path->msglim = NETIUCV_QUEUELEN_DEFAULT;
|
||||||
path->flags = 0;
|
path->flags = 0;
|
||||||
rc = iucv_path_accept(path, &netiucv_handler, NULL, conn);
|
rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
|
IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc);
|
||||||
return;
|
return;
|
||||||
|
@ -854,7 +886,7 @@ static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
|
||||||
|
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
fsm_deltimer(&conn->timer);
|
fsm_deltimer(&conn->timer);
|
||||||
iucv_path_sever(conn->path, NULL);
|
iucv_path_sever(conn->path, conn->userdata);
|
||||||
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,9 +899,9 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
|
|
||||||
fsm_deltimer(&conn->timer);
|
fsm_deltimer(&conn->timer);
|
||||||
iucv_path_sever(conn->path, NULL);
|
iucv_path_sever(conn->path, conn->userdata);
|
||||||
dev_info(privptr->dev, "The peer interface of the IUCV device"
|
dev_info(privptr->dev, "The peer z/VM guest %s has closed the "
|
||||||
" has closed the connection\n");
|
"connection\n", netiucv_printuser(conn));
|
||||||
IUCV_DBF_TEXT(data, 2,
|
IUCV_DBF_TEXT(data, 2,
|
||||||
"conn_action_connsever: Remote dropped connection\n");
|
"conn_action_connsever: Remote dropped connection\n");
|
||||||
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
||||||
|
@ -886,8 +918,6 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
|
|
||||||
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
||||||
IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
|
|
||||||
netdev->name, conn->userid);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must set the state before calling iucv_connect because the
|
* We must set the state before calling iucv_connect because the
|
||||||
|
@ -897,8 +927,11 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
|
||||||
|
|
||||||
fsm_newstate(fi, CONN_STATE_SETUPWAIT);
|
fsm_newstate(fi, CONN_STATE_SETUPWAIT);
|
||||||
conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
|
conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL);
|
||||||
|
IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n",
|
||||||
|
netdev->name, netiucv_printuser(conn));
|
||||||
|
|
||||||
rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
|
rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid,
|
||||||
NULL, iucvMagic, conn);
|
NULL, conn->userdata, conn);
|
||||||
switch (rc) {
|
switch (rc) {
|
||||||
case 0:
|
case 0:
|
||||||
netdev->tx_queue_len = conn->path->msglim;
|
netdev->tx_queue_len = conn->path->msglim;
|
||||||
|
@ -908,13 +941,13 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
|
||||||
case 11:
|
case 11:
|
||||||
dev_warn(privptr->dev,
|
dev_warn(privptr->dev,
|
||||||
"The IUCV device failed to connect to z/VM guest %s\n",
|
"The IUCV device failed to connect to z/VM guest %s\n",
|
||||||
netiucv_printname(conn->userid));
|
netiucv_printname(conn->userid, 8));
|
||||||
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
dev_warn(privptr->dev,
|
dev_warn(privptr->dev,
|
||||||
"The IUCV device failed to connect to the peer on z/VM"
|
"The IUCV device failed to connect to the peer on z/VM"
|
||||||
" guest %s\n", netiucv_printname(conn->userid));
|
" guest %s\n", netiucv_printname(conn->userid, 8));
|
||||||
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
fsm_newstate(fi, CONN_STATE_STARTWAIT);
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
|
@ -927,7 +960,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
|
||||||
dev_err(privptr->dev,
|
dev_err(privptr->dev,
|
||||||
"z/VM guest %s has too many IUCV connections"
|
"z/VM guest %s has too many IUCV connections"
|
||||||
" to connect with the IUCV device\n",
|
" to connect with the IUCV device\n",
|
||||||
netiucv_printname(conn->userid));
|
netiucv_printname(conn->userid, 8));
|
||||||
fsm_newstate(fi, CONN_STATE_CONNERR);
|
fsm_newstate(fi, CONN_STATE_CONNERR);
|
||||||
break;
|
break;
|
||||||
case 15:
|
case 15:
|
||||||
|
@ -972,7 +1005,7 @@ static void conn_action_stop(fsm_instance *fi, int event, void *arg)
|
||||||
netiucv_purge_skb_queue(&conn->collect_queue);
|
netiucv_purge_skb_queue(&conn->collect_queue);
|
||||||
if (conn->path) {
|
if (conn->path) {
|
||||||
IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
|
IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n");
|
||||||
iucv_path_sever(conn->path, iucvMagic);
|
iucv_path_sever(conn->path, conn->userdata);
|
||||||
kfree(conn->path);
|
kfree(conn->path);
|
||||||
conn->path = NULL;
|
conn->path = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1090,7 +1123,8 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
|
||||||
fsm_newstate(fi, DEV_STATE_RUNNING);
|
fsm_newstate(fi, DEV_STATE_RUNNING);
|
||||||
dev_info(privptr->dev,
|
dev_info(privptr->dev,
|
||||||
"The IUCV device has been connected"
|
"The IUCV device has been connected"
|
||||||
" successfully to %s\n", privptr->conn->userid);
|
" successfully to %s\n",
|
||||||
|
netiucv_printuser(privptr->conn));
|
||||||
IUCV_DBF_TEXT(setup, 3,
|
IUCV_DBF_TEXT(setup, 3,
|
||||||
"connection is up and running\n");
|
"connection is up and running\n");
|
||||||
break;
|
break;
|
||||||
|
@ -1452,7 +1486,55 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr,
|
||||||
struct netiucv_priv *priv = dev_get_drvdata(dev);
|
struct netiucv_priv *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
IUCV_DBF_TEXT(trace, 5, __func__);
|
IUCV_DBF_TEXT(trace, 5, __func__);
|
||||||
return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid));
|
return sprintf(buf, "%s\n", netiucv_printuser(priv->conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int netiucv_check_user(const char *buf, size_t count, char *username,
|
||||||
|
char *userdata)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
p = strchr(buf, '.');
|
||||||
|
if ((p && ((count > 26) ||
|
||||||
|
((p - buf) > 8) ||
|
||||||
|
(buf + count - p > 18))) ||
|
||||||
|
(!p && (count > 9))) {
|
||||||
|
IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) {
|
||||||
|
if (isalnum(*p) || *p == '$') {
|
||||||
|
username[i] = toupper(*p);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*p == '\n')
|
||||||
|
/* trailing lf, grr */
|
||||||
|
break;
|
||||||
|
IUCV_DBF_TEXT_(setup, 2,
|
||||||
|
"conn_write: invalid character %02x\n", *p);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
while (i < 8)
|
||||||
|
username[i++] = ' ';
|
||||||
|
username[8] = '\0';
|
||||||
|
|
||||||
|
if (*p == '.') {
|
||||||
|
p++;
|
||||||
|
for (i = 0; i < 16 && *p; i++, p++) {
|
||||||
|
if (*p == '\n')
|
||||||
|
break;
|
||||||
|
userdata[i] = toupper(*p);
|
||||||
|
}
|
||||||
|
while (i > 0 && i < 16)
|
||||||
|
userdata[i++] = ' ';
|
||||||
|
} else
|
||||||
|
memcpy(userdata, iucvMagic_ascii, 16);
|
||||||
|
userdata[16] = '\0';
|
||||||
|
ASCEBC(userdata, 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t user_write(struct device *dev, struct device_attribute *attr,
|
static ssize_t user_write(struct device *dev, struct device_attribute *attr,
|
||||||
|
@ -1460,36 +1542,15 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
|
||||||
{
|
{
|
||||||
struct netiucv_priv *priv = dev_get_drvdata(dev);
|
struct netiucv_priv *priv = dev_get_drvdata(dev);
|
||||||
struct net_device *ndev = priv->conn->netdev;
|
struct net_device *ndev = priv->conn->netdev;
|
||||||
char *p;
|
|
||||||
char *tmp;
|
|
||||||
char username[9];
|
char username[9];
|
||||||
int i;
|
char userdata[17];
|
||||||
|
int rc;
|
||||||
struct iucv_connection *cp;
|
struct iucv_connection *cp;
|
||||||
|
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
if (count > 9) {
|
rc = netiucv_check_user(buf, count, username, userdata);
|
||||||
IUCV_DBF_TEXT_(setup, 2,
|
if (rc)
|
||||||
"%d is length of username\n", (int) count);
|
return rc;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = strsep((char **) &buf, "\n");
|
|
||||||
for (i = 0, p = tmp; i < 8 && *p; i++, p++) {
|
|
||||||
if (isalnum(*p) || (*p == '$')) {
|
|
||||||
username[i]= toupper(*p);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (*p == '\n') {
|
|
||||||
/* trailing lf, grr */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
IUCV_DBF_TEXT_(setup, 2,
|
|
||||||
"username: invalid character %c\n", *p);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
while (i < 8)
|
|
||||||
username[i++] = ' ';
|
|
||||||
username[8] = '\0';
|
|
||||||
|
|
||||||
if (memcmp(username, priv->conn->userid, 9) &&
|
if (memcmp(username, priv->conn->userid, 9) &&
|
||||||
(ndev->flags & (IFF_UP | IFF_RUNNING))) {
|
(ndev->flags & (IFF_UP | IFF_RUNNING))) {
|
||||||
|
@ -1499,15 +1560,17 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
|
||||||
}
|
}
|
||||||
read_lock_bh(&iucv_connection_rwlock);
|
read_lock_bh(&iucv_connection_rwlock);
|
||||||
list_for_each_entry(cp, &iucv_connection_list, list) {
|
list_for_each_entry(cp, &iucv_connection_list, list) {
|
||||||
if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) {
|
if (!strncmp(username, cp->userid, 9) &&
|
||||||
|
!strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) {
|
||||||
read_unlock_bh(&iucv_connection_rwlock);
|
read_unlock_bh(&iucv_connection_rwlock);
|
||||||
IUCV_DBF_TEXT_(setup, 2, "user_write: Connection "
|
IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s "
|
||||||
"to %s already exists\n", username);
|
"already exists\n", netiucv_printuser(cp));
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_unlock_bh(&iucv_connection_rwlock);
|
read_unlock_bh(&iucv_connection_rwlock);
|
||||||
memcpy(priv->conn->userid, username, 9);
|
memcpy(priv->conn->userid, username, 9);
|
||||||
|
memcpy(priv->conn->userdata, userdata, 17);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1537,7 +1600,8 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr,
|
||||||
bs1 = simple_strtoul(buf, &e, 0);
|
bs1 = simple_strtoul(buf, &e, 0);
|
||||||
|
|
||||||
if (e && (!isspace(*e))) {
|
if (e && (!isspace(*e))) {
|
||||||
IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e);
|
IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n",
|
||||||
|
*e);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (bs1 > NETIUCV_BUFSIZE_MAX) {
|
if (bs1 > NETIUCV_BUFSIZE_MAX) {
|
||||||
|
@ -1864,7 +1928,8 @@ static void netiucv_unregister_device(struct device *dev)
|
||||||
* Add it to the list of netiucv connections;
|
* Add it to the list of netiucv connections;
|
||||||
*/
|
*/
|
||||||
static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
|
static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
|
||||||
char *username)
|
char *username,
|
||||||
|
char *userdata)
|
||||||
{
|
{
|
||||||
struct iucv_connection *conn;
|
struct iucv_connection *conn;
|
||||||
|
|
||||||
|
@ -1893,6 +1958,8 @@ static struct iucv_connection *netiucv_new_connection(struct net_device *dev,
|
||||||
fsm_settimer(conn->fsm, &conn->timer);
|
fsm_settimer(conn->fsm, &conn->timer);
|
||||||
fsm_newstate(conn->fsm, CONN_STATE_INVALID);
|
fsm_newstate(conn->fsm, CONN_STATE_INVALID);
|
||||||
|
|
||||||
|
if (userdata)
|
||||||
|
memcpy(conn->userdata, userdata, 17);
|
||||||
if (username) {
|
if (username) {
|
||||||
memcpy(conn->userid, username, 9);
|
memcpy(conn->userid, username, 9);
|
||||||
fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
|
fsm_newstate(conn->fsm, CONN_STATE_STOPPED);
|
||||||
|
@ -1919,6 +1986,7 @@ out:
|
||||||
*/
|
*/
|
||||||
static void netiucv_remove_connection(struct iucv_connection *conn)
|
static void netiucv_remove_connection(struct iucv_connection *conn)
|
||||||
{
|
{
|
||||||
|
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
write_lock_bh(&iucv_connection_rwlock);
|
write_lock_bh(&iucv_connection_rwlock);
|
||||||
list_del_init(&conn->list);
|
list_del_init(&conn->list);
|
||||||
|
@ -1926,7 +1994,7 @@ static void netiucv_remove_connection(struct iucv_connection *conn)
|
||||||
fsm_deltimer(&conn->timer);
|
fsm_deltimer(&conn->timer);
|
||||||
netiucv_purge_skb_queue(&conn->collect_queue);
|
netiucv_purge_skb_queue(&conn->collect_queue);
|
||||||
if (conn->path) {
|
if (conn->path) {
|
||||||
iucv_path_sever(conn->path, iucvMagic);
|
iucv_path_sever(conn->path, conn->userdata);
|
||||||
kfree(conn->path);
|
kfree(conn->path);
|
||||||
conn->path = NULL;
|
conn->path = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1985,7 +2053,7 @@ static void netiucv_setup_netdevice(struct net_device *dev)
|
||||||
/**
|
/**
|
||||||
* Allocate and initialize everything of a net device.
|
* Allocate and initialize everything of a net device.
|
||||||
*/
|
*/
|
||||||
static struct net_device *netiucv_init_netdevice(char *username)
|
static struct net_device *netiucv_init_netdevice(char *username, char *userdata)
|
||||||
{
|
{
|
||||||
struct netiucv_priv *privptr;
|
struct netiucv_priv *privptr;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
@ -2004,7 +2072,7 @@ static struct net_device *netiucv_init_netdevice(char *username)
|
||||||
if (!privptr->fsm)
|
if (!privptr->fsm)
|
||||||
goto out_netdev;
|
goto out_netdev;
|
||||||
|
|
||||||
privptr->conn = netiucv_new_connection(dev, username);
|
privptr->conn = netiucv_new_connection(dev, username, userdata);
|
||||||
if (!privptr->conn) {
|
if (!privptr->conn) {
|
||||||
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
|
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n");
|
||||||
goto out_fsm;
|
goto out_fsm;
|
||||||
|
@ -2022,47 +2090,31 @@ out_netdev:
|
||||||
static ssize_t conn_write(struct device_driver *drv,
|
static ssize_t conn_write(struct device_driver *drv,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
const char *p;
|
|
||||||
char username[9];
|
char username[9];
|
||||||
int i, rc;
|
char userdata[17];
|
||||||
|
int rc;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
struct netiucv_priv *priv;
|
struct netiucv_priv *priv;
|
||||||
struct iucv_connection *cp;
|
struct iucv_connection *cp;
|
||||||
|
|
||||||
IUCV_DBF_TEXT(trace, 3, __func__);
|
IUCV_DBF_TEXT(trace, 3, __func__);
|
||||||
if (count>9) {
|
rc = netiucv_check_user(buf, count, username, userdata);
|
||||||
IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n");
|
if (rc)
|
||||||
return -EINVAL;
|
return rc;
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, p = buf; i < 8 && *p; i++, p++) {
|
|
||||||
if (isalnum(*p) || *p == '$') {
|
|
||||||
username[i] = toupper(*p);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (*p == '\n')
|
|
||||||
/* trailing lf, grr */
|
|
||||||
break;
|
|
||||||
IUCV_DBF_TEXT_(setup, 2,
|
|
||||||
"conn_write: invalid character %c\n", *p);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
while (i < 8)
|
|
||||||
username[i++] = ' ';
|
|
||||||
username[8] = '\0';
|
|
||||||
|
|
||||||
read_lock_bh(&iucv_connection_rwlock);
|
read_lock_bh(&iucv_connection_rwlock);
|
||||||
list_for_each_entry(cp, &iucv_connection_list, list) {
|
list_for_each_entry(cp, &iucv_connection_list, list) {
|
||||||
if (!strncmp(username, cp->userid, 9)) {
|
if (!strncmp(username, cp->userid, 9) &&
|
||||||
|
!strncmp(userdata, cp->userdata, 17)) {
|
||||||
read_unlock_bh(&iucv_connection_rwlock);
|
read_unlock_bh(&iucv_connection_rwlock);
|
||||||
IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection "
|
IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s "
|
||||||
"to %s already exists\n", username);
|
"already exists\n", netiucv_printuser(cp));
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_unlock_bh(&iucv_connection_rwlock);
|
read_unlock_bh(&iucv_connection_rwlock);
|
||||||
|
|
||||||
dev = netiucv_init_netdevice(username);
|
dev = netiucv_init_netdevice(username, userdata);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
|
IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -2083,8 +2135,9 @@ static ssize_t conn_write(struct device_driver *drv,
|
||||||
if (rc)
|
if (rc)
|
||||||
goto out_unreg;
|
goto out_unreg;
|
||||||
|
|
||||||
dev_info(priv->dev, "The IUCV interface to %s has been"
|
dev_info(priv->dev, "The IUCV interface to %s has been established "
|
||||||
" established successfully\n", netiucv_printname(username));
|
"successfully\n",
|
||||||
|
netiucv_printuser(priv->conn));
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue