]> git.neil.brown.name Git - history.git/commitdiff
ncpfs: Proper handling of watchdog packets.
authorPetr Vandrovec <vandrove@vc.cvut.cz>
Fri, 20 Sep 2002 23:40:34 +0000 (01:40 +0200)
committerPetr Vandrovec <vandrove@vc.cvut.cz>
Fri, 20 Sep 2002 23:40:34 +0000 (01:40 +0200)
ncpfs: Add support for packet signatures when using TCP transport.

fs/ncpfs/inode.c
fs/ncpfs/ncpsign_kernel.c
fs/ncpfs/ncpsign_kernel.h
fs/ncpfs/sock.c
include/linux/ncp.h
include/linux/ncp_fs_sb.h

index c42a026b1448d7133306ea2acc0ae1374485b6ed..a6b7004205a392c473e674bef7757bfa2c85e43c 100644 (file)
@@ -31,6 +31,8 @@
 
 #include <linux/ncp_fs.h>
 
+#include <net/sock.h>
+
 #include "ncplib_kernel.h"
 #include "getopt.h"
 
@@ -284,6 +286,16 @@ ncp_delete_inode(struct inode *inode)
        clear_inode(inode);
 }
 
+static void ncp_stop_tasks(struct ncp_server *server) {
+       struct sock* sk = server->ncp_sock->sk;
+               
+       sk->error_report = server->error_report;
+       sk->data_ready = server->data_ready;
+       sk->write_space = server->write_space;
+       del_timer_sync(&server->timeout_tm);
+       flush_scheduled_tasks();
+}
+
 static const struct ncp_option ncp_opts[] = {
        { "uid",        OPT_INT,        'u' },
        { "gid",        OPT_INT,        'g' },
@@ -464,6 +476,8 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
        memset(server, 0, sizeof(*server));
 
        server->ncp_filp = ncp_filp;
+       server->ncp_sock = sock;
+       
 /*     server->lock = 0;       */
        init_MUTEX(&server->sem);
        server->packet = NULL;
@@ -501,6 +515,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
 
        server->dentry_ttl = 0; /* no caching */
 
+       INIT_LIST_HEAD(&server->tx.requests);
+       init_MUTEX(&server->rcv.creq_sem);
+       server->tx.creq = NULL;
+       server->rcv.creq = NULL;
+       server->data_ready = sock->sk->data_ready;
+       server->write_space = sock->sk->write_space;
+       server->error_report = sock->sk->error_report;
+       sock->sk->user_data = server;
+
+       init_timer(&server->timeout_tm);
 #undef NCP_PACKET_SIZE
 #define NCP_PACKET_SIZE 131072
        error = -ENOMEM;
@@ -509,6 +533,22 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
        if (server->packet == NULL)
                goto out_nls;
 
+       sock->sk->data_ready = ncp_tcp_data_ready;
+       sock->sk->error_report = ncp_tcp_error_report;
+       if (sock->type == SOCK_STREAM) {
+               server->rcv.ptr = (unsigned char*)&server->rcv.buf;
+               server->rcv.len = 10;
+               server->rcv.state = 0;
+               INIT_TQUEUE(&server->rcv.tq, ncp_tcp_rcv_proc, server);
+               INIT_TQUEUE(&server->tx.tq, ncp_tcp_tx_proc, server);
+               sock->sk->write_space = ncp_tcp_write_space;
+       } else {
+               INIT_TQUEUE(&server->rcv.tq, ncpdgram_rcv_proc, server);
+               INIT_TQUEUE(&server->timeout_tq, ncpdgram_timeout_proc, server);
+               server->timeout_tm.data = (unsigned long)server;
+               server->timeout_tm.function = ncpdgram_timeout_call;
+       }
+
        ncp_lock_server(server);
        error = ncp_connect(server);
        ncp_unlock_server(server);
@@ -583,6 +623,7 @@ out_disconnect:
        ncp_disconnect(server);
        ncp_unlock_server(server);
 out_packet:
+       ncp_stop_tasks(server);
        vfree(server->packet);
 out_nls:
 #ifdef CONFIG_NCPFS_NLS
@@ -610,6 +651,8 @@ static void ncp_put_super(struct super_block *sb)
        ncp_disconnect(server);
        ncp_unlock_server(server);
 
+       ncp_stop_tasks(server);
+
 #ifdef CONFIG_NCPFS_NLS
        /* unload the NLS charsets */
        if (server->nls_vol)
index 7183ba4bcb92a91a5ef56fa81277ffaa696b9021..e81bbe7deaa9fa986f7796486c493033eda7fb5c 100644 (file)
@@ -93,19 +93,19 @@ static void nwsign(char *r_data1, char *r_data2, char *outdata) {
 
 /* Make a signature for the current packet and add it at the end of the */
 /* packet. */
-void sign_packet(struct ncp_server *server, int *size) {
- char data[64];
+void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) {
      unsigned char data[64];
 
memset(data,0,64);
memcpy(data,server->sign_root,8);
- PUT_LE32(data+8,(*size));
- memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1,
-  min_t(unsigned int,(*size)-sizeof(struct ncp_request_header)+1,52));
-
nwsign(server->sign_last,data,server->sign_last);
-
memcpy(server->packet+(*size),server->sign_last,8);
(*size)+=8;
      memcpy(data, server->sign_root, 8);
      *(__u32*)(data + 8) = totalsize;
+       if (size < 52) {
+               memcpy(data + 12, packet, size);
+               memset(data + 12 + size, 0, 52 - size);
+       } else {
              memcpy(data + 12, packet, 52);
+       }
      nwsign(server->sign_last, data, server->sign_last);
      memcpy(sign_buff, server->sign_last, 8);
 }
 
 #endif /* CONFIG_NCPFS_PACKET_SIGNING */
index 141338fb5ad29190016acfda017d6c21659792e1..45254e5cf263111c0ee387fe8ab40736b10a329a 100644 (file)
 
 #include <linux/ncp_fs.h>
 
-void sign_packet(struct ncp_server *server, int *size);
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+void __sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff);
+#endif
+
+static inline size_t sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff) {
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+       if (server->sign_active) {
+               __sign_packet(server, data, size, totalsize, sign_buff);
+               return 8;
+       }
+#endif
+       return 0;
+}
 
 #endif
index 59753377c7f2fed3643b33b9ac0dc2e987a44c14..92304ec7103bbcfe3fcb5f552754ff54786bc416 100644 (file)
 
 #include <linux/ncp_fs.h>
 
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
 #include "ncpsign_kernel.h"
-#endif
 
 static int _recv(struct socket *sock, unsigned char *ubuf, int size,
                 unsigned flags)
 {
        struct iovec iov;
        struct msghdr msg;
-       struct scm_cookie scm;
-
-       memset(&scm, 0, sizeof(scm));
 
        iov.iov_base = ubuf;
        iov.iov_len = size;
@@ -50,15 +45,14 @@ static int _recv(struct socket *sock, unsigned char *ubuf, int size,
        msg.msg_control = NULL;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
-       return sock->ops->recvmsg(sock, &msg, size, flags, &scm);
+
+       return sock_recvmsg(sock, &msg, size, flags);
 }
 
-static int _send(struct socket *sock, const void *buff, int len)
+static inline int _send(struct socket *sock, const void *buff, int len)
 {
        struct iovec iov;
        struct msghdr msg;
-       struct scm_cookie scm;
-       int err;
 
        iov.iov_base = (void *) buff;
        iov.iov_len = len;
@@ -70,353 +64,599 @@ static int _send(struct socket *sock, const void *buff, int len)
        msg.msg_iovlen = 1;
        msg.msg_flags = 0;
 
-       err = scm_send(sock, &msg, &scm);
-       if (err < 0) {
-               return err;
+       return sock_sendmsg(sock, &msg, len);
+}
+
+struct ncp_request_reply {
+       struct list_head req;
+       wait_queue_head_t wq;
+       struct ncp_reply_header* reply_buf;
+       size_t datalen;
+       int result;
+       enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status;
+       struct iovec* tx_ciov;
+       size_t tx_totallen;
+       size_t tx_iovlen;
+       struct iovec tx_iov[3];
+       u_int16_t tx_type;
+       u_int32_t sign[6];
+};
+
+void ncp_tcp_data_ready(struct sock *sk, int len) {
+       struct ncp_server *server = sk->user_data;
+
+       server->data_ready(sk, len);
+       schedule_task(&server->rcv.tq);
+}
+
+void ncp_tcp_error_report(struct sock *sk) {
+       struct ncp_server *server = sk->user_data;
+       
+       server->error_report(sk);
+       schedule_task(&server->rcv.tq);
+}
+
+void ncp_tcp_write_space(struct sock *sk) {
+       struct ncp_server *server = sk->user_data;
+       
+       /* We do not need any locking: we first set tx.creq, and then we do sendmsg,
+          not vice versa... */
+       server->write_space(sk);
+       if (server->tx.creq) {
+               schedule_task(&server->tx.tq);
        }
-       err = sock->ops->sendmsg(sock, &msg, len, &scm);
-       scm_destroy(&scm);
-       return err;
 }
 
-static int do_ncp_rpc_call(struct ncp_server *server, int size,
-               struct ncp_reply_header* reply_buf, int max_reply_size)
-{
-       struct file *file;
-       struct socket *sock;
+void ncpdgram_timeout_call(unsigned long v) {
+       struct ncp_server *server = (void*)v;
+       
+       schedule_task(&server->timeout_tq);
+}
+
+static inline void ncp_finish_request(struct ncp_request_reply *req, int result) {
+       req->result = result;
+       req->status = RQ_DONE;
+       wake_up_all(&req->wq);
+}
+
+static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) {
+       struct ncp_request_reply *req;
+
+       ncp_invalidate_conn(server);
+       del_timer(&server->timeout_tm);
+       while (!list_empty(&server->tx.requests)) {
+               req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
+               
+               list_del_init(&req->req);
+               if (req == aborted) {
+                       ncp_finish_request(req, err);
+               } else {
+                       ncp_finish_request(req, -EIO);
+               }
+       }
+       req = server->rcv.creq;
+       if (req) {
+               server->rcv.creq = NULL;
+               if (req == aborted) {
+                       ncp_finish_request(req, err);
+               } else {
+                       ncp_finish_request(req, -EIO);
+               }
+               server->rcv.ptr = NULL;
+               server->rcv.state = 0;
+       }
+       req = server->tx.creq;
+       if (req) {
+               server->tx.creq = NULL;
+               if (req == aborted) {
+                       ncp_finish_request(req, err);
+               } else {
+                       ncp_finish_request(req, -EIO);
+               }
+       }
+}
+
+static inline int get_conn_number(struct ncp_reply_header *rp) {
+       return rp->conn_low | (rp->conn_high << 8);
+}
+
+static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
+       /* If req is done, we got signal, but we also received answer... */
+       switch (req->status) {
+               case RQ_IDLE:
+               case RQ_DONE:
+                       break;
+               case RQ_QUEUED:
+                       list_del_init(&req->req);
+                       ncp_finish_request(req, err);
+                       break;
+               case RQ_INPROGRESS:
+                       __abort_ncp_connection(server, req, err);
+                       break;
+       }
+}
+
+static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
+       down(&server->rcv.creq_sem);
+       __ncp_abort_request(server, req, err);
+       up(&server->rcv.creq_sem);
+}
+
+static inline void __ncptcp_abort(struct ncp_server *server) {
+       __abort_ncp_connection(server, NULL, 0);
+}
+
+static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) {
+       struct msghdr msg;
+       
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_control = NULL;
+       msg.msg_iov = req->tx_ciov;
+       msg.msg_iovlen = req->tx_iovlen;
+       msg.msg_flags = MSG_DONTWAIT;
+       return sock_sendmsg(sock, &msg, req->tx_totallen);
+}
+
+static void __ncptcp_try_send(struct ncp_server *server) {
+       struct ncp_request_reply *rq;
+       struct msghdr msg;
+       struct iovec* iov;
        int result;
-       char *start = server->packet;
-       poll_table wait_table;
-       int init_timeout, max_timeout;
-       int timeout;
-       int retrans;
-       int major_timeout_seen;
-       int acknowledge_seen;
-       int n;
 
-       /* We have to check the result, so store the complete header */
-       struct ncp_request_header request =
-       *((struct ncp_request_header *) (server->packet));
-
-       struct ncp_reply_header reply;
-
-       file = server->ncp_filp;
-       sock = SOCKET_I(file->f_dentry->d_inode);
-
-       init_timeout = server->m.time_out;
-       max_timeout = NCP_MAX_RPC_TIMEOUT;
-       retrans = server->m.retry_count;
-       major_timeout_seen = 0;
-       acknowledge_seen = 0;
-
-       for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) {
-               /*
-               DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
-                        htonl(server->m.serv_addr.sipx_network),
-                        server->m.serv_addr.sipx_node[0],
-                        server->m.serv_addr.sipx_node[1],
-                        server->m.serv_addr.sipx_node[2],
-                        server->m.serv_addr.sipx_node[3],
-                        server->m.serv_addr.sipx_node[4],
-                        server->m.serv_addr.sipx_node[5],
-                        ntohs(server->m.serv_addr.sipx_port));
-               */
-               DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
-                        "seq: %d",
-                        request.type,
-                        (request.conn_high << 8) + request.conn_low,
-                        request.sequence);
-               DDPRINTK(" func: %d\n",
-                        request.function);
-
-               result = _send(sock, (void *) start, size);
+       rq = server->tx.creq;
+       if (!rq) {
+               return;
+       }
+
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_control = NULL;
+       msg.msg_iov = rq->tx_ciov;
+       msg.msg_iovlen = rq->tx_iovlen;
+       msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
+       result = sock_sendmsg(server->ncp_sock, &msg, rq->tx_totallen);
+       if (result == -EAGAIN) {
+               return;
+       }
+       if (result < 0) {
+               printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
+               __ncp_abort_request(server, rq, result);
+               return;
+       }
+       if (result >= rq->tx_totallen) {
+               server->rcv.creq = rq;
+               server->tx.creq = NULL;
+               return;
+       }
+       rq->tx_totallen -= result;
+       iov = rq->tx_ciov;
+       while (iov->iov_len <= result) {
+               result -= iov->iov_len;
+               iov++;
+               rq->tx_iovlen--;
+       }
+       iov->iov_len -= result;
+       rq->tx_ciov = iov;
+}
+
+static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h) {
+       req->status = RQ_INPROGRESS;
+       h->conn_low = server->connection;
+       h->conn_high = server->connection >> 8;
+       h->sequence = ++server->sequence;
+}
+       
+static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+       size_t signlen;
+       struct ncp_request_header* h;
+       
+       req->tx_ciov = req->tx_iov + 1;
+
+       h = req->tx_iov[1].iov_base;
+       ncp_init_header(server, req, h);
+       signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1, 
+                       req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1,
+                       cpu_to_le32(req->tx_totallen), req->sign);
+       if (signlen) {
+               req->tx_ciov[1].iov_base = req->sign;
+               req->tx_ciov[1].iov_len = signlen;
+               req->tx_iovlen += 1;
+               req->tx_totallen += signlen;
+       }
+       server->rcv.creq = req;
+       server->timeout_last = server->m.time_out;
+       server->timeout_retries = server->m.retry_count;
+       ncpdgram_send(server->ncp_sock, req);
+       mod_timer(&server->timeout_tm, jiffies + server->m.time_out);
+}
+
+#define NCP_TCP_XMIT_MAGIC     (0x446D6454)
+#define NCP_TCP_XMIT_VERSION   (1)
+#define NCP_TCP_RCVD_MAGIC     (0x744E6350)
+
+static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+       size_t signlen;
+       struct ncp_request_header* h;
+
+       req->tx_ciov = req->tx_iov;
+       h = req->tx_iov[1].iov_base;
+       ncp_init_header(server, req, h);
+       signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
+                       req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1,
+                       cpu_to_be32(req->tx_totallen + 24), req->sign + 4) + 16;
+
+       req->sign[0] = htonl(NCP_TCP_XMIT_MAGIC);
+       req->sign[1] = htonl(req->tx_totallen + signlen);
+       req->sign[2] = htonl(NCP_TCP_XMIT_VERSION);
+       req->sign[3] = htonl(req->datalen + 8);
+       req->tx_iov[0].iov_base = req->sign;
+       req->tx_iov[0].iov_len = signlen;
+       req->tx_iovlen += 1;
+       req->tx_totallen += signlen;
+
+       server->tx.creq = req;
+       __ncptcp_try_send(server);
+}
+
+static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
+       if (server->ncp_sock->type == SOCK_STREAM)
+               ncptcp_start_request(server, req);
+       else
+               ncpdgram_start_request(server, req);
+}
+
+static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req) {
+       down(&server->rcv.creq_sem);
+       if (!ncp_conn_valid(server)) {
+               up(&server->rcv.creq_sem);
+               printk(KERN_ERR "ncpfs: tcp: Server died\n");
+               return -EIO;
+       }
+       if (server->tx.creq || server->rcv.creq) {
+               req->status = RQ_QUEUED;
+               list_add_tail(&req->req, &server->tx.requests);
+               up(&server->rcv.creq_sem);
+               return 0;
+       }
+       __ncp_start_request(server, req);
+       up(&server->rcv.creq_sem);
+       return 0;
+}
+
+static void __ncp_next_request(struct ncp_server *server) {
+       struct ncp_request_reply *req;
+
+       server->rcv.creq = NULL;
+       if (list_empty(&server->tx.requests)) {
+               return;
+       }
+       req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
+       list_del_init(&req->req);
+       __ncp_start_request(server, req);
+}
+
+static void __ncpdgram_rcv_proc(void *s) {
+       struct ncp_server *server = s;
+       struct socket* sock;
+       
+       sock = server->ncp_sock;
+       
+       while (1) {
+               struct ncp_reply_header reply;
+               int result;
+
+               result = _recv(sock, (void*)&reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
                if (result < 0) {
-                       printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result);
-                       return result;
+                       break;
                }
-             re_select:
-               poll_initwait(&wait_table);
-               /* mb() is not necessary because ->poll() will serialize
-                  instructions adding the wait_table waitqueues in the
-                  waitqueue-head before going to calculate the mask-retval. */
-               __set_current_state(TASK_INTERRUPTIBLE);
-               if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) {
-                       int timed_out;
-                       if (timeout > max_timeout) {
-                               /* JEJB/JSP 2/7/94
-                                * This is useful to see if the system is
-                                * hanging */
-                               if (acknowledge_seen == 0) {
-                                       printk(KERN_WARNING "NCP max timeout\n");
+               if (result >= sizeof(reply)) {
+                       struct ncp_request_reply *req;
+       
+                       if (reply.type == NCP_WATCHDOG) {
+                               unsigned char buf[10];
+
+                               if (server->connection != get_conn_number(&reply)) {
+                                       goto drop;
                                }
-                               timeout = max_timeout;
-                       }
-                       timed_out = !schedule_timeout(timeout);
-                       poll_freewait(&wait_table);
-                       current->state = TASK_RUNNING;
-                       if (signal_pending(current)) {
-                               result = -ERESTARTSYS;
-                               break;
-                       }
-                       if(wait_table.error) {
-                               result = wait_table.error;
-                               break;
-                       }
-                       if (timed_out) {
-                               if (n < retrans)
+                               result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
+                               if (result < 0) {
+                                       DPRINTK("recv failed with %d\n", result);
                                        continue;
-                               if (server->m.flags & NCP_MOUNT_SOFT) {
-                                       printk(KERN_WARNING "NCP server not responding\n");
-                                       result = -EIO;
-                                       break;
                                }
-                               n = 0;
-                               timeout = init_timeout;
-                               if (init_timeout < max_timeout)
-                                       init_timeout <<= 1;
-                               if (!major_timeout_seen) {
-                                       printk(KERN_WARNING "NCP server not responding\n");
+                               if (result < 10) {
+                                       DPRINTK("too short (%u) watchdog packet\n", result);
+                                       continue;
+                               }
+                               if (buf[9] != '?') {
+                                       DPRINTK("bad signature (%02X) in watchdog packet\n", buf[9]);
+                                       continue;
                                }
-                               major_timeout_seen = 1;
+                               buf[9] = 'Y';
+                               _send(sock, buf, sizeof(buf));
                                continue;
                        }
-               } else {
-                       poll_freewait(&wait_table);
+                       down(&server->rcv.creq_sem);            
+                       req = server->rcv.creq;
+                       if (req && (req->tx_type == NCP_ALLOC_SLOT_REQUEST || (server->sequence == reply.sequence && 
+                                       server->connection == get_conn_number(&reply)))) {
+                               if (reply.type == NCP_POSITIVE_ACK) {
+                                       server->timeout_retries = server->m.retry_count;
+                                       server->timeout_last = NCP_MAX_RPC_TIMEOUT;
+                                       mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT);
+                               } else if (reply.type == NCP_REPLY) {
+                                       result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT);
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+                                       if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) {
+                                               if (result < 8 + 8) {
+                                                       result = -EIO;
+                                               } else {
+                                                       result -= 8;
+                                               }
+                                       }
+#endif
+                                       del_timer(&server->timeout_tm);
+                                       server->rcv.creq = NULL;
+                                       ncp_finish_request(req, result);
+                                       __ncp_next_request(server);
+                                       up(&server->rcv.creq_sem);
+                                       continue;
+                               }
+                       }
+                       up(&server->rcv.creq_sem);
                }
-               current->state = TASK_RUNNING;
+drop:;         
+               _recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT);
+       }
+}
 
-               /* Get the header from the next packet using a peek, so keep it
-                * on the recv queue.  If it is wrong, it will be some reply
-                * we don't now need, so discard it */
-               result = _recv(sock, (void *) &reply, sizeof(reply),
-                              MSG_PEEK | MSG_DONTWAIT);
-               if (result < 0) {
-                       if (result == -EAGAIN) {
-                               DDPRINTK("ncp_rpc_call: bad select ready\n");
-                               goto re_select;
-                       }
-                       if (result == -ECONNREFUSED) {
-                               DPRINTK("ncp_rpc_call: server playing coy\n");
-                               goto re_select;
+void ncpdgram_rcv_proc(void *s) {
+       mm_segment_t fs;
+       struct ncp_server *server = s;
+       
+       fs = get_fs();
+       set_fs(get_ds());
+       __ncpdgram_rcv_proc(server);
+       set_fs(fs);
+}
+
+static void __ncpdgram_timeout_proc(struct ncp_server *server) {
+       /* If timer is pending, we are processing another request... */
+       if (!timer_pending(&server->timeout_tm)) {
+               struct ncp_request_reply* req;
+               
+               req = server->rcv.creq;
+               if (req) {
+                       int timeout;
+                       
+                       if (server->m.flags & NCP_MOUNT_SOFT) {
+                               if (server->timeout_retries-- == 0) {
+                                       __ncp_abort_request(server, req, -ETIMEDOUT);
+                                       return;
+                               }
                        }
-                       if (result != -ERESTARTSYS) {
-                               printk(KERN_ERR "ncp_rpc_call: recv error = %d\n",
-                                      -result);
+                       /* Ignore errors */
+                       ncpdgram_send(server->ncp_sock, req);
+                       timeout = server->timeout_last << 1;
+                       if (timeout > NCP_MAX_RPC_TIMEOUT) {
+                               timeout = NCP_MAX_RPC_TIMEOUT;
                        }
-                       break;
-               }
-               if ((result == sizeof(reply))
-                   && (reply.type == NCP_POSITIVE_ACK)) {
-                       /* Throw away the packet */
-                       DPRINTK("ncp_rpc_call: got positive acknowledge\n");
-                       _recv(sock, (void *) &reply, sizeof(reply),
-                             MSG_DONTWAIT);
-                       n = 0;
-                       timeout = max_timeout;
-                       acknowledge_seen = 1;
-                       goto re_select;
+                       server->timeout_last = timeout;
+                       mod_timer(&server->timeout_tm, jiffies + timeout);
                }
-               DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
-                        "seq: %d\n",
-                        reply.type,
-                        (reply.conn_high << 8) + reply.conn_low,
-                        reply.task,
-                        reply.sequence);
-
-               if ((result >= sizeof(reply))
-                   && (reply.type == NCP_REPLY)
-                   && ((request.type == NCP_ALLOC_SLOT_REQUEST)
-                       || ((reply.sequence == request.sequence)
-                           && (reply.conn_low == request.conn_low)
-/* seem to get wrong task from NW311 && (reply.task      == request.task) */
-                           && (reply.conn_high == request.conn_high)))) {
-                       if (major_timeout_seen)
-                               printk(KERN_NOTICE "NCP server OK\n");
-                       break;
-               }
-               /* JEJB/JSP 2/7/94
-                * we have xid mismatch, so discard the packet and start
-                * again.  What a hack! but I can't call recvfrom with
-                * a null buffer yet. */
-               _recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT);
-
-               DPRINTK("ncp_rpc_call: reply mismatch\n");
-               goto re_select;
-       }
-       /* 
-        * we have the correct reply, so read into the correct place and
-        * return it
-        */
-       result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT);
-       if (result < 0) {
-               printk(KERN_WARNING "NCP: notice message: result=%d\n", result);
-       } else if (result < sizeof(struct ncp_reply_header)) {
-               printk(KERN_ERR "NCP: just caught a too small read memory size..., "
-                      "email to NET channel\n");
-               printk(KERN_ERR "NCP: result=%d\n", result);
-               result = -EIO;
        }
+}
 
-       return result;
+void ncpdgram_timeout_proc(void *s) {
+       mm_segment_t fs;
+       struct ncp_server *server = s;
+       
+       fs = get_fs();
+       set_fs(get_ds());
+       down(&server->rcv.creq_sem);
+       __ncpdgram_timeout_proc(server);
+       up(&server->rcv.creq_sem);
+       set_fs(fs);
+}
+
+static inline void ncp_init_req(struct ncp_request_reply* req) {
+       init_waitqueue_head(&req->wq);
+       req->status = RQ_IDLE;
 }
 
 static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) {
-       poll_table wait_table;
-       struct file *file;
-       struct socket *sock;
-       int init_timeout;
-       size_t dataread;
-       int result = 0;
-       
-       file = server->ncp_filp;
-       sock = SOCKET_I(file->f_dentry->d_inode);
+       int result;
        
-       dataread = 0;
+       if (buffer) {
+               result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
+       } else {
+               static unsigned char dummy[1024];
+                       
+               if (len > sizeof(dummy)) {
+                       len = sizeof(dummy);
+               }
+               result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT);
+       }
+       if (result < 0) {
+               return result;
+       }
+       if (result > len) {
+               printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %u)\n", result, len);
+               return -EIO;                    
+       }
+       return result;
+}      
 
-       init_timeout = server->m.time_out * 20;
-       
-       /* hard-mounted volumes have no timeout, except connection close... */
-       if (!(server->m.flags & NCP_MOUNT_SOFT))
-               init_timeout = 0x7FFF0000;
-
-       while (len) {
-               poll_initwait(&wait_table);
-               /* mb() is not necessary because ->poll() will serialize
-                  instructions adding the wait_table waitqueues in the
-                  waitqueue-head before going to calculate the mask-retval. */
-               __set_current_state(TASK_INTERRUPTIBLE);
-               if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) {
-                       init_timeout = schedule_timeout(init_timeout);
-                       poll_freewait(&wait_table);
-                       current->state = TASK_RUNNING;
-                       if (signal_pending(current)) {
-                               return -ERESTARTSYS;
+static int __ncptcp_rcv_proc(struct ncp_server *server) {
+       /* We have to check the result, so store the complete header */
+       while (1) {
+               int result;
+               struct ncp_request_reply *req;
+               int datalen;
+               int type;
+
+               while (server->rcv.len) {
+                       result = do_tcp_rcv(server, server->rcv.ptr, server->rcv.len);
+                       if (result == -EAGAIN) {
+                               return 0;
                        }
-                       if (!init_timeout) {
+                       if (result <= 0) {
+                               req = server->rcv.creq;
+                               if (req) {
+                                       __ncp_abort_request(server, req, -EIO);
+                               } else {
+                                       __ncptcp_abort(server);
+                               }
+                               if (result < 0) {
+                                       printk(KERN_ERR "ncpfs: tcp: error in recvmsg: %d\n", result);
+                               } else {
+                                       DPRINTK(KERN_ERR "ncpfs: tcp: EOF\n");
+                               }
                                return -EIO;
                        }
-                       if(wait_table.error) {
-                               return wait_table.error;
+                       if (server->rcv.ptr) {
+                               server->rcv.ptr += result;
                        }
-               } else {
-                       poll_freewait(&wait_table);
-               }
-               current->state = TASK_RUNNING;
-
-               result = _recv(sock, buffer, len, MSG_DONTWAIT);
-               if (result < 0) {
-                       if (result == -EAGAIN) {
-                               DDPRINTK("ncpfs: tcp: bad select ready\n");
-                               continue;
-                       }
-                       return result;
-               }
-               if (result == 0) {
-                       printk(KERN_ERR "ncpfs: tcp: EOF on socket\n");
-                       return -EIO;
+                       server->rcv.len -= result;
                }
-               if (result > len) {
-                       printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n");
-                       return -EIO;                    
+               switch (server->rcv.state) {
+                       case 0:
+                               if (server->rcv.buf.magic != htonl(NCP_TCP_RCVD_MAGIC)) {
+                                       printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(server->rcv.buf.magic));
+                                       __ncptcp_abort(server);
+                                       return -EIO;
+                               }
+                               datalen = ntohl(server->rcv.buf.len) & 0x0FFFFFFF;
+                               if (datalen < 10) {
+                                       printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
+                                       __ncptcp_abort(server);
+                                       return -EIO;
+                               }
+#ifdef CONFIG_NCPFS_PACKET_SIGNING                             
+                               if (server->sign_active) {
+                                       if (datalen < 18) {
+                                               printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
+                                               __ncptcp_abort(server);
+                                               return -EIO;
+                                       }
+                                       server->rcv.buf.len = datalen - 8;
+                                       server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1;
+                                       server->rcv.len = 8;
+                                       server->rcv.state = 4;
+                                       break;
+                               }
+#endif                         
+                               type = ntohs(server->rcv.buf.type);
+cont:;                         
+                               if (type != NCP_REPLY) {
+                                       DPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", type);
+skipdata2:;
+                                       server->rcv.state = 2;
+skipdata:;
+                                       server->rcv.ptr = NULL;
+                                       server->rcv.len = datalen - 10;
+                                       break;
+                               }
+                               req = server->rcv.creq;
+                               if (!req) {
+                                       DPRINTK(KERN_ERR "ncpfs: Reply without appropriate request\n");
+                                       goto skipdata2;
+                               }
+                               if (datalen > req->datalen + 8) {
+                                       printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d (expected at most %d)\n", datalen, req->datalen + 8);
+                                       server->rcv.state = 3;
+                                       goto skipdata;
+                               }
+                               req->datalen = datalen - 8;
+                               req->reply_buf->type = NCP_REPLY;
+                               server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2;
+                               server->rcv.len = datalen - 10;
+                               server->rcv.state = 1;
+                               break;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING                             
+                       case 4:
+                               datalen = server->rcv.buf.len;
+                               type = ntohs(server->rcv.buf.type2);
+                               goto cont;
+#endif
+                       case 1:
+                               req = server->rcv.creq;
+                               if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) {
+                                       if (req->reply_buf->sequence != server->sequence) {
+                                               printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
+                                               __ncp_abort_request(server, req, -EIO);
+                                               return -EIO;
+                                       }
+                                       if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) {
+                                               printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
+                                               __ncp_abort_request(server, req, -EIO);
+                                               return -EIO;
+                                       }
+                               }
+                               ncp_finish_request(req, req->datalen);
+                       nextreq:;
+                               __ncp_next_request(server);
+                       case 2:
+                               server->rcv.ptr = (unsigned char*)&server->rcv.buf;
+                               server->rcv.len = 10;
+                               server->rcv.state = 0;
+                               break;
+                       case 3:
+                               ncp_finish_request(server->rcv.creq, -EIO);
+                               goto nextreq;
                }
-               dataread += result;
-               buffer += result;
-               len -= result;
        }
-       return 0;
-}      
+}
 
-#define NCP_TCP_XMIT_MAGIC     (0x446D6454)
-#define NCP_TCP_XMIT_VERSION   (1)
-#define NCP_TCP_RCVD_MAGIC     (0x744E6350)
+void ncp_tcp_rcv_proc(void *s) {
+       mm_segment_t fs;
+       struct ncp_server *server = s;
+
+       fs = get_fs();
+       set_fs(get_ds());
+       down(&server->rcv.creq_sem);
+       __ncptcp_rcv_proc(server);
+       up(&server->rcv.creq_sem);
+       set_fs(fs);
+       return;
+}
 
-static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size,
+void ncp_tcp_tx_proc(void *s) {
+       mm_segment_t fs;
+       struct ncp_server *server = s;
+       
+       fs = get_fs();
+       set_fs(get_ds());
+       down(&server->rcv.creq_sem);
+       __ncptcp_try_send(server);
+       up(&server->rcv.creq_sem);
+       set_fs(fs);
+       return;
+}
+
+static int do_ncp_rpc_call(struct ncp_server *server, int size,
                struct ncp_reply_header* reply_buf, int max_reply_size)
 {
-       struct file *file;
-       struct socket *sock;
        int result;
-       struct iovec iov[2];
-       struct msghdr msg;
-       struct scm_cookie scm;
-       __u32 ncptcp_rcvd_hdr[2];
-       __u32 ncptcp_xmit_hdr[4];
-       int   datalen;
-
-       /* We have to check the result, so store the complete header */
-       struct ncp_request_header request =
-       *((struct ncp_request_header *) (server->packet));
-
-       file = server->ncp_filp;
-       sock = SOCKET_I(file->f_dentry->d_inode);
-       
-       ncptcp_xmit_hdr[0] = htonl(NCP_TCP_XMIT_MAGIC);
-       ncptcp_xmit_hdr[1] = htonl(size + 16);
-       ncptcp_xmit_hdr[2] = htonl(NCP_TCP_XMIT_VERSION);
-       ncptcp_xmit_hdr[3] = htonl(max_reply_size + 8);
-
-       DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
-                "seq: %d",
-                request.type,
-                (request.conn_high << 8) + request.conn_low,
-                request.sequence);
-       DDPRINTK(" func: %d\n",
-                request.function);
-
-       iov[1].iov_base = (void *) server->packet;
-       iov[1].iov_len = size;
-       iov[0].iov_base = ncptcp_xmit_hdr;
-       iov[0].iov_len = 16;
-       msg.msg_name = NULL;
-       msg.msg_namelen = 0;
-       msg.msg_control = NULL;
-       msg.msg_iov = iov;
-       msg.msg_iovlen = 2;
-       msg.msg_flags = MSG_NOSIGNAL;
-
-       result = scm_send(sock, &msg, &scm);
-       if (result < 0) {
-               return result;
-       }
-       result = sock->ops->sendmsg(sock, &msg, size + 16, &scm);
-       scm_destroy(&scm);
+       struct ncp_request_reply req;
+
+       ncp_init_req(&req);
+       req.reply_buf = reply_buf;
+       req.datalen = max_reply_size;
+       req.tx_iov[1].iov_base = (void *) server->packet;
+       req.tx_iov[1].iov_len = size;
+       req.tx_iovlen = 1;
+       req.tx_totallen = size;
+       req.tx_type = *(u_int16_t*)server->packet;
+
+       result = ncp_add_request(server, &req);
        if (result < 0) {
-               printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
                return result;
        }
-rstrcv:
-       result = do_tcp_rcv(server, ncptcp_rcvd_hdr, 8);
-       if (result)
-               return result;
-       if (ncptcp_rcvd_hdr[0] != htonl(NCP_TCP_RCVD_MAGIC)) {
-               printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(ncptcp_rcvd_hdr[0]));
-               return -EIO;
+       if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) {
+               ncp_abort_request(server, &req, -EIO);
        }
-       datalen = ntohl(ncptcp_rcvd_hdr[1]);
-       if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) {
-               printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
-               return -EIO;
-       }
-       datalen -= 8;
-       result = do_tcp_rcv(server, reply_buf, datalen);
-       if (result)
-               return result;
-       if (reply_buf->type != NCP_REPLY) {
-               DDPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", reply_buf->type);
-               goto rstrcv;
-       }
-       if (request.type == NCP_ALLOC_SLOT_REQUEST)
-               return datalen;
-       if (reply_buf->sequence != request.sequence) {
-               printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
-               return -EIO;
-       }
-       if ((reply_buf->conn_low != request.conn_low) ||
-           (reply_buf->conn_high != request.conn_high)) {
-               printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
-               return -EIO;
-       }
-       return datalen;
+       return req.result;
 }
 
 /*
@@ -426,8 +666,6 @@ rstrcv:
 static int ncp_do_request(struct ncp_server *server, int size,
                void* reply, int max_reply_size)
 {
-       struct file *file;
-       struct socket *sock;
        int result;
 
        if (server->lock == 0) {
@@ -435,21 +673,10 @@ static int ncp_do_request(struct ncp_server *server, int size,
                return -EIO;
        }
        if (!ncp_conn_valid(server)) {
+               printk(KERN_ERR "ncpfs: Connection invalid!\n");
                return -EIO;
        }
-#ifdef CONFIG_NCPFS_PACKET_SIGNING
-       if (server->sign_active)
        {
-               sign_packet(server, &size);
-       }
-#endif /* CONFIG_NCPFS_PACKET_SIGNING */
-       file = server->ncp_filp;
-       sock = SOCKET_I(file->f_dentry->d_inode);
-       /* N.B. this isn't needed ... check socket type? */
-       if (!sock) {
-               printk(KERN_ERR "ncp_rpc_call: socki_lookup failed\n");
-               result = -EBADF;
-       } else {
                mm_segment_t fs;
                sigset_t old_set;
                unsigned long mask, flags;
@@ -478,10 +705,7 @@ static int ncp_do_request(struct ncp_server *server, int size,
                fs = get_fs();
                set_fs(get_ds());
 
-               if (sock->type == SOCK_STREAM)
-                       result = do_ncp_tcp_rpc_call(server, size, reply, max_reply_size);
-               else
-                       result = do_ncp_rpc_call(server, size, reply, max_reply_size);
+               result = do_ncp_rpc_call(server, size, reply, max_reply_size);
 
                set_fs(fs);
 
@@ -510,20 +734,13 @@ int ncp_request2(struct ncp_server *server, int function,
 {
        struct ncp_request_header *h;
        struct ncp_reply_header* reply = rpl;
-       int request_size = server->current_size
-                        - sizeof(struct ncp_request_header);
        int result;
 
        h = (struct ncp_request_header *) (server->packet);
        if (server->has_subfunction != 0) {
-               *(__u16 *) & (h->data[0]) = htons(request_size - 2);
+               *(__u16 *) & (h->data[0]) = htons(server->current_size - sizeof(*h) - 2);
        }
        h->type = NCP_REQUEST;
-
-       server->sequence += 1;
-       h->sequence = server->sequence;
-       h->conn_low = (server->connection) & 0xff;
-       h->conn_high = ((server->connection) & 0xff00) >> 8;
        /*
         * The server shouldn't know or care what task is making a
         * request, so we always use the same task number.
@@ -531,7 +748,7 @@ int ncp_request2(struct ncp_server *server, int function,
        h->task = 2; /* (current->pid) & 0xff; */
        h->function = function;
 
-       result = ncp_do_request(server, request_size + sizeof(*h), reply, size);
+       result = ncp_do_request(server, server->current_size, reply, size);
        if (result < 0) {
                DPRINTK("ncp_request_error: %d\n", result);
                goto out;
@@ -554,20 +771,17 @@ int ncp_connect(struct ncp_server *server)
        struct ncp_request_header *h;
        int result;
 
+       server->connection = 0xFFFF;
+       server->sequence = 255;
+
        h = (struct ncp_request_header *) (server->packet);
        h->type = NCP_ALLOC_SLOT_REQUEST;
-
-       server->sequence = 0;
-       h->sequence     = server->sequence;
-       h->conn_low     = 0xff;
-       h->conn_high    = 0xff;
        h->task         = 2; /* see above */
        h->function     = 0;
 
        result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
        if (result < 0)
                goto out;
-       server->sequence = 0;
        server->connection = h->conn_low + (h->conn_high * 256);
        result = 0;
 out:
@@ -580,11 +794,6 @@ int ncp_disconnect(struct ncp_server *server)
 
        h = (struct ncp_request_header *) (server->packet);
        h->type = NCP_DEALLOC_SLOT_REQUEST;
-
-       server->sequence += 1;
-       h->sequence     = server->sequence;
-       h->conn_low     = (server->connection) & 0xff;
-       h->conn_high    = ((server->connection) & 0xff00) >> 8;
        h->task         = 2; /* see above */
        h->function     = 0;
 
index fa20708267b932f27678d70d71fe0b412280ed63..92062440abb06583ef1b48b6aedad976f721e773 100644 (file)
@@ -30,6 +30,7 @@ struct ncp_request_header {
 };
 
 #define NCP_REPLY                (0x3333)
+#define NCP_WATCHDOG            (0x3E3E)
 #define NCP_POSITIVE_ACK         (0x9999)
 
 struct ncp_reply_header {
index 7bc6b250cdd5bed9fc10991e81758b3c548011c0..375c8511ca25dd7ce42e10871aa8070efaa7537b 100644 (file)
@@ -13,6 +13,8 @@
 
 #ifdef __KERNEL__
 
+#include <linux/tqueue.h>
+
 #define NCP_DEFAULT_OPTIONS 0          /* 2 for packet signatures */
 
 struct ncp_server {
@@ -24,6 +26,7 @@ struct ncp_server {
        __u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
 
        struct file *ncp_filp;  /* File pointer to ncp socket */
+       struct socket *ncp_sock;/* ncp socket */
 
        u8 sequence;
        u8 task;
@@ -79,8 +82,50 @@ struct ncp_server {
 
        /* miscellaneous */
        unsigned int flags;
+
+       spinlock_t requests_lock;       /* Lock accesses to tx.requests, tx.creq and rcv.creq when STREAM mode */
+
+       void (*data_ready)(struct sock* sk, int len);
+       void (*error_report)(struct sock* sk);
+       void (*write_space)(struct sock* sk);   /* STREAM mode only */
+       struct {
+               struct tq_struct tq;            /* STREAM/DGRAM: data/error ready */
+               struct ncp_request_reply* creq; /* STREAM/DGRAM: awaiting reply from this request */
+               struct semaphore creq_sem;      /* DGRAM only: lock accesses to rcv.creq */
+
+               unsigned int state;             /* STREAM only: receiver state */
+               struct {
+                       __u32 magic __attribute__((packed));
+                       __u32 len __attribute__((packed));
+                       __u16 type __attribute__((packed));
+                       __u16 p1 __attribute__((packed));
+                       __u16 p2 __attribute__((packed));
+                       __u16 p3 __attribute__((packed));
+                       __u16 type2 __attribute__((packed));
+               } buf;                          /* STREAM only: temporary buffer */
+               unsigned char* ptr;             /* STREAM only: pointer to data */
+               size_t len;                     /* STREAM only: length of data to receive */
+       } rcv;
+       struct {
+               struct list_head requests;      /* STREAM only: queued requests */
+               struct tq_struct tq;            /* STREAM only: transmitter ready */
+               struct ncp_request_reply* creq; /* STREAM only: currently transmitted entry */
+       } tx;
+       struct timer_list timeout_tm;           /* DGRAM only: timeout timer */
+       struct tq_struct timeout_tq;            /* DGRAM only: associated queue, we run timers from process context */
+       int timeout_last;                       /* DGRAM only: current timeout length */
+       int timeout_retries;                    /* DGRAM only: retries left */
 };
 
+extern void ncp_tcp_rcv_proc(void *server);
+extern void ncp_tcp_tx_proc(void *server);
+extern void ncpdgram_rcv_proc(void *server);
+extern void ncpdgram_timeout_proc(void *server);
+extern void ncpdgram_timeout_call(unsigned long server);
+extern void ncp_tcp_data_ready(struct sock* sk, int len);
+extern void ncp_tcp_write_space(struct sock* sk);
+extern void ncp_tcp_error_report(struct sock* sk);
+
 #define NCP_FLAG_UTF8  1
 
 #define NCP_CLR_FLAG(server, flag)     ((server)->flags &= ~(flag))