📄 ip_to_dlpi.c
字号:
/************************************************************************* * * Copyright (C) 1998-2003 The Software Group Limited. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, * MA 02139, USA. * *************************************************************************//* * This driver provides a mapping between Linux IP and an underlying * DLPI streams driver. The DLPI messages are the same as what * IP on a System V system would expect. * * TSG uses this driver to allow it's IP/X.25 software to work on * Linux. Since that driver is called xinet, you can probably guess * how the name "ip2xinet" came about. * * It creates a device "/dev/ip2xinet". Open this device and link it * to the driver below. * * This driver supports up to 8 network devices: ip2x0-7. * They can be configured using the ifconfig program. * * A few things are required in order to make this work: * 1) You must create a static arp entry in the table indicating * the remote machines that we are directly connected to. If * you look at ip2xinet_hw_tx, you will see how this address ends up * being used. * eg. arp -s 10.1.1.2 0A:01:01:02:00:00 * * 2) The driver underneath must have the routing information * so that it can figure out how to send the IP datagram * that it has received. This step is specific to that driver. */#include <linux/version.h>#include <linux/config.h>#ifdef CONFIG_MODVERSIONS#include <linux/modversions.h>#endif#include <linux/module.h>#include <asm/param.h>#include <linux/sched.h> #include <linux/kernel.h> /* printk() */#include <linux/errno.h> /* error codes */#include <linux/types.h> /* size_t */#include <linux/interrupt.h> /* mark_bh */#include <linux/netdevice.h> /* struct net_device, and other headers */#include <linux/etherdevice.h> /* eth_type_trans */#include <linux/ip.h> /* struct iphdr */#include <linux/tcp.h> /* struct tcphdr */#include <linux/skbuff.h>#include <linux/if_arp.h>#include <net/arp.h>#include <sys/stream.h>#include <sys/dlpi.h>#include <sys/lismem.h>#include <sys/lislocks.h>/************************************************************************* Module Information *************************************************************************//* * Note: We are labeling the module license as "GPL and additional rights". * This is said to be equivalent to GPL for symbol exporting purposes and * is also supposed to span LGPL. */#if defined(MODULE_LICENSE)MODULE_LICENSE("GPL and additional rights");#endif#if defined(MODULE_AUTHOR)MODULE_AUTHOR("The Software Group Ltd.");#endif#if defined(MODULE_DESCRIPTION)MODULE_DESCRIPTION("Linux IP to Streams driver");#endif/* version dependencies have been confined to a separate file *//* * Macros to help debugging */#undef PDEBUG /* undef it, just in case */#ifdef IP2XINET_DEBUG# ifdef __KERNEL__ /* This one if debugging is on, and kernel space */# define PDEBUG(fmt, args...) printk( KERN_DEBUG "ip2xinet: " fmt, ## args)# else /* This one for user space */# define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)# endif#else# define PDEBUG(fmt, args...) /* not debugging: nothing */#endif#undef PDEBUGG#define PDEBUGG(fmt, args...) /* nothing: it's a placeholder *//* * This structure is private to each device. It is used to pass * packets in and out, so there is place for a packet */#define NUMIP2XINET 8struct ip2xinet_priv { struct net_device_stats stats; int state; int packetlen; u8 *packetdata;} ip2xinet_private[NUMIP2XINET];struct ip2xinet_state { int ip2x_dlstate; /* TSG: DLPI user state */ int myminor; /* store getminor result */ queue_t *lowerq; /* lower write queue corresponding * to myminor */ queue_t *readq; /* upper read queue for my minor * (just in case we care) */ int index; /* Unique ID of lower stream */ } ip2xinet_status;extern struct net_device ip2xinet_devs[];/* These are the flags in the statusword */#define IP2XINETM_ID 568#define UNLINKED 0x20 /* our addition to DLPI states */int ip2xinetdevflag = 0;int ip2xinetopen(queue_t *, dev_t *, int, int, cred_t *);int ip2xinetclose(queue_t *, int, cred_t *);int ip2xinetuwput(queue_t* q, mblk_t* mp);int ip2xinetlrput(queue_t* q, mblk_t* mp);int ip2xinetursrv(queue_t *q);int ip2xinetlwsrv(queue_t *q);void ip2xinet_rx(struct net_device *dev, struct sk_buff *skb);int ip2xinet_send_down_bind(queue_t *q);int init_linuxip(void);void cleanup_linuxip(void);int ip2xinet_num_ip_opened;lis_spin_lock_t *ip2xinet_lock;int ip2_m_number;int ip2xinetinit(void); struct module_info ip2xinetminfo ={ IP2XINETM_ID, "ip2xinet", 0, 8192, 8192, 1024};struct qinit ip2xineturinit ={ /* upper read */ NULL, ip2xinetursrv, ip2xinetopen, ip2xinetclose, NULL, &ip2xinetminfo, NULL};struct qinit ip2xinetuwinit ={ /* upper write */ ip2xinetuwput, NULL, ip2xinetopen, ip2xinetclose, NULL, &ip2xinetminfo, NULL};struct qinit ip2xinetlrinit ={ /* lower read */ ip2xinetlrput, NULL, NULL, NULL, NULL, &ip2xinetminfo, NULL};struct qinit ip2xinetlwinit ={ /* lower write */ NULL, ip2xinetlwsrv, NULL, NULL, NULL, &ip2xinetminfo, NULL};struct streamtab ip2xinetinfo ={ &ip2xineturinit, &ip2xinetuwinit, &ip2xinetlrinit, &ip2xinetlwinit};static int ip2xinet_numopen = 0; /* How many times ip2xinet was opened as * a STREAMS device */char kernel_version[] = UTS_RELEASE;int ip2xinetinit(void){ if (0 == init_linuxip()) return 1; else return 0;}/************************************************************************ * * Function Name: ip2xinetopen * Title: IP2XINET driver Open Routine * * Description: * Open a queue (device) * * Arguments: * q - read queue pointer * dev - major/minor device number * flag - file open flags * sflag - STREAM open flags * * Return Value: * dev - minor device * ************************************************************************/int ip2xinetopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp){ mblk_t *bp; minor_t minor; struct stroptions *sop; lis_flags_t oldpl; /* * already open */ if (sflag != CLONEOPEN) return ENXIO; lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); /* Can only open one time */ if (ip2xinet_numopen) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return ENXIO; } else ip2xinet_numopen = 1; if (q->q_count != 0) printk("ip2x level:q_count is %lu",q->q_count); /* * Set up the flow control parameters and send them up to the stream head. */ minor = getminor(*devp); if ((bp = allocb(sizeof(struct stroptions), BPRI_LO)) == NULL) { lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); printk("ip2xinetopen: allocb failed"); return ENOMEM; } ip2xinet_status.myminor = minor; ip2xinet_status.ip2x_dlstate = DL_UNATTACHED; /* These are dummy to keep track of the fact the device is open */ q->q_ptr = (char *) &ip2xinet_numopen; WR(q)->q_ptr = (char *) &ip2xinet_numopen; lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); bp->b_datap->db_type = M_SETOPTS; bp->b_wptr += sizeof(struct stroptions); sop = (struct stroptions *) bp->b_rptr; sop->so_flags = SO_HIWAT | SO_LOWAT; sop->so_hiwat = ip2xinetminfo.mi_hiwat; sop->so_lowat = ip2xinetminfo.mi_lowat; putnext(q, bp); MOD_INC_USE_COUNT; *devp = makedevice(getmajor(*devp), 0); return 0;}/************************************************************************ * * Function Name: ip2xinetclose * Title: IP2XINET Driver Close Routine * * Description: * Close an ip2xinet queue (device) * * Arguments: * q - read queue pointer * * Return Value: * none * ************************************************************************/int ip2xinetclose(queue_t *q, int flag, cred_t *credp){ lis_flags_t oldpl; if (q->q_ptr == NULL) return (0); lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); ip2xinet_numopen = 0; flushq(WR(q), FLUSHALL); q->q_ptr = NULL; WR(q)->q_ptr = NULL; MOD_DEC_USE_COUNT; lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return (0);}/************************************************************************ * * Function Name: ip2xinetuwput * Title: IP2XINET Upper Write Put Routine * * Description: * Handle messages coming downstream. Should be no data in these * just stupid stuff like flushq * * Arguments: * q - upper write queue * mp - pointer to an message block * * Return Value: * none * ************************************************************************/int ip2xinetuwput(queue_t *q, mblk_t *mp){ int i; lis_flags_t oldpl; lis_spin_lock_irqsave(ip2xinet_lock, &oldpl); switch (mp->b_datap->db_type) { case M_FLUSH: if (*mp->b_rptr & FLUSHW) { flushq(q, FLUSHALL); qenable(q); *mp->b_rptr &= ~FLUSHW; } if (*mp->b_rptr & FLUSHR) { flushq(RD(q), FLUSHALL); putq(RD(q), mp); } else freemsg(mp); break; case M_IOCTL: /* Process at least the I_LINK, I_UNLINK */ /* THINKME: Failure to correctly process I_LINK/I_UNLINK while * returning correctly a nack to stream head will * leave us in a possibly totally screwed up DLPI state * from which we have to somehow recover. The possible * problematic states are DL_UNBOUND, any DL_PENDING states * * Note: if we stay in UNATTACHED on I_LINK failure or in * IDLE on I_UNLINK failure we're ok as long as the * private data structure stuff is consistent with * the state */ { struct iocblk *iocp; mblk_t *nmp; dl_attach_req_t *attach; struct linkblk *lp; iocp = (struct iocblk *) mp->b_rptr; #ifdef DEBUG pkt_debug(X25DBIOCTL) KPRINTF("%s size %d\n", x25dbiocmsg(iocp->ioc_cmd), x25dbmsgsize(mp)); #endif switch ((unsigned) iocp->ioc_cmd) { case I_LINK: iocp->ioc_error = 0; iocp->ioc_rval = 0; iocp->ioc_count = 0; lp = (struct linkblk *) mp->b_cont->b_rptr; /* Use only one xinet queue for all devices */ ip2xinet_status.lowerq = lp->l_qbot; ip2xinet_status.index = lp->l_index; /* Only one read q to get data from xinet */ ip2xinet_status.readq = RD(q); /* These are dummy ones to indicate the queues are being used */ ip2xinet_status.lowerq->q_ptr = (char *) &ip2xinet_numopen; RD(ip2xinet_status.lowerq)->q_ptr = (char *) &ip2xinet_numopen; if ((nmp = allocb(sizeof(union DL_primitives), BPRI_LO)) == NULL) { iocp->ioc_error = ENOSR; mp->b_datap->db_type = M_IOCNAK; putq(RD(q), mp); lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); printk("pktioctl: I_LINK failed: allocb failed"); return (0); } /* * Setup and send an ATTACH */ nmp->b_datap->db_type = M_PROTO; nmp->b_wptr += DL_ATTACH_REQ_SIZE; attach = (dl_attach_req_t *) nmp->b_rptr; attach->dl_primitive = DL_ATTACH_REQ; attach->dl_ppa = ip2xinet_status.myminor; ip2xinet_status.ip2x_dlstate = DL_ATTACH_PENDING; /* experience shows that an I_LINKed queue needs to * be enabled so that the service routine will be run. */ qenable(ip2xinet_status.lowerq); putq(ip2xinet_status.lowerq, nmp); /* all went well */ mp->b_datap->db_type = M_IOCACK; putq(RD(q), mp); break; case I_UNLINK: { struct linkblk *lp; iocp->ioc_error = 0; iocp->ioc_rval = 0; iocp->ioc_count = 0; lp = (struct linkblk *) mp->b_cont->b_rptr; /* * Ignore the DLPI state, the stack is being torn * down regardless. */ ip2xinet_status.ip2x_dlstate = UNLINKED; /* can't transmit any more */ for (i = 0; i<NUMIP2XINET; i++) { struct ip2xinet_priv *privptr = &ip2xinet_private[i]; if (privptr->state == 1) netif_stop_queue(&(ip2xinet_devs[i])); } flushq(q, FLUSHALL); flushq(RD(lp->l_qbot), FLUSHALL); ip2xinet_status.readq = NULL; ip2xinet_status.lowerq = NULL; mp->b_datap->db_type = M_IOCACK; putq(RD(q), mp); break; } default: iocp->ioc_error = EINVAL; mp->b_datap->db_type = M_IOCNAK; putq(RD(q), mp); break; } } break; case M_DATA: case M_PCPROTO: case M_PROTO: default: printk("ip2xinetuwput: unexpected type=0x%x", mp->b_datap->db_type); freemsg(mp); break; } lis_spin_unlock_irqrestore(ip2xinet_lock, &oldpl); return (0);}/************************************************************************ * * Function Name: ip2xinetursrv * Title: IP2XINET Upper Read Service routine * * Description: * Send all of the messages on this queue up to the next driver. * We don't worry about doing flow control. This is used because in * the places where we might want to call putnext, we call putq * instead. * ************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -