📄 isdn_ppp.c
字号:
/* $Id: isdn_ppp.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include <linux/config.h>#include <linux/isdn.h>#include <linux/poll.h>#include <linux/ppp-comp.h>#ifdef CONFIG_IPPP_FILTER#include <linux/filter.h>#endif#include "isdn_common.h"#include "isdn_ppp.h"#include "isdn_net.h"#ifndef PPP_IPX#define PPP_IPX 0x002b#endif/* Prototypes */static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);static int isdn_ppp_closewait(int slot);static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto);static int isdn_ppp_if_get_unit(char *namebuf);static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);static struct sk_buff *isdn_ppp_decompress(struct sk_buff *, struct ippp_struct *,struct ippp_struct *,int *proto);static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb,int proto);static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, struct ippp_struct *is,struct ippp_struct *master,int type);static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb);/* New CCP stuff */static void isdn_ppp_ccp_kickup(struct ippp_struct *is);static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, unsigned char code, unsigned char id, unsigned char *data, int len);static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);static void isdn_ppp_ccp_reset_free(struct ippp_struct *is);static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, unsigned char id);static void isdn_ppp_ccp_timer_callback(unsigned long closure);static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is, unsigned char id);static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is, struct isdn_ppp_resetparams *rp);static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, unsigned char id);#ifdef CONFIG_ISDN_MPPstatic ippp_bundle * isdn_ppp_bundle_arr = NULL; static int isdn_ppp_mp_bundle_array_init(void);static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb);static void isdn_ppp_mp_cleanup( isdn_net_local * lp );static int isdn_ppp_bundle(struct ippp_struct *, int unit);#endif /* CONFIG_ISDN_MPP */ char *isdn_ppp_revision = "$Revision: 1.1.2.3 $";static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];static struct isdn_ppp_compressor *ipc_head = NULL;/* * frame log (debug) */static voidisdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot){ int cnt, j, i; char buf[80]; if (len < maxlen) maxlen = len; for (i = 0, cnt = 0; cnt < maxlen; i++) { for (j = 0; j < 16 && cnt < maxlen; j++, cnt++) sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]); printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf); }}/* * unbind isdn_net_local <=> ippp-device * note: it can happen, that we hangup/free the master before the slaves * in this case we bind another lp to the master device */intisdn_ppp_free(isdn_net_local * lp){ struct ippp_struct *is; if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: ppp_slot(%d) out of range\n", __FUNCTION__, lp->ppp_slot); return 0; }#ifdef CONFIG_ISDN_MPP spin_lock(&lp->netdev->pb->lock);#endif isdn_net_rm_from_bundle(lp);#ifdef CONFIG_ISDN_MPP if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */ isdn_ppp_mp_cleanup(lp); lp->netdev->pb->ref_ct--; spin_unlock(&lp->netdev->pb->lock);#endif /* CONFIG_ISDN_MPP */ if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n", __FUNCTION__, lp->ppp_slot); return 0; } is = ippp_table[lp->ppp_slot]; if ((is->state & IPPP_CONNECT)) isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ else if (is->state & IPPP_ASSIGNED) is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */ if (is->debug & 0x1) printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp); is->lp = NULL; /* link is down .. set lp to NULL */ lp->ppp_slot = -1; /* is this OK ?? */ return 0;}/* * bind isdn_net_local <=> ippp-device * * This function is allways called with holding dev->lock so * no additional lock is needed */intisdn_ppp_bind(isdn_net_local * lp){ int i; int unit = 0; struct ippp_struct *is; int retval; if (lp->pppbind < 0) { /* device bounded to ippp device ? */ isdn_net_dev *net_dev = dev->netdev; char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ memset(exclusive, 0, ISDN_MAX_CHANNELS); while (net_dev) { /* step through net devices to find exclusive minors */ isdn_net_local *lp = net_dev->local; if (lp->pppbind >= 0) exclusive[lp->pppbind] = 1; net_dev = net_dev->next; } /* * search a free device / slot */ for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */ break; } } } else { for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (ippp_table[i]->minor == lp->pppbind && (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN) break; } } if (i >= ISDN_MAX_CHANNELS) { printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n"); retval = -1; goto out; } unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */ if (unit < 0) { printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name); retval = -1; goto out; } lp->ppp_slot = i; is = ippp_table[i]; is->lp = lp; is->unit = unit; is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */#ifdef CONFIG_ISDN_MPP retval = isdn_ppp_mp_init(lp, NULL); if (retval < 0) goto out;#endif /* CONFIG_ISDN_MPP */ retval = lp->ppp_slot; out: return retval;}/* * kick the ipppd on the device * (wakes up daemon after B-channel connect) */voidisdn_ppp_wakeup_daemon(isdn_net_local * lp){ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: ppp_slot(%d) out of range\n", __FUNCTION__, lp->ppp_slot); return; } ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);}/* * there was a hangup on the netdevice * force wakeup of the ippp device * go into 'device waits for release' state */static intisdn_ppp_closewait(int slot){ struct ippp_struct *is; if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { printk(KERN_ERR "%s: slot(%d) out of range\n", __FUNCTION__, slot); return 0; } is = ippp_table[slot]; if (is->state) wake_up_interruptible(&is->wq); is->state = IPPP_CLOSEWAIT; return 1;}/* * isdn_ppp_find_slot / isdn_ppp_free_slot */static intisdn_ppp_get_slot(void){ int i; for (i = 0; i < ISDN_MAX_CHANNELS; i++) { if (!ippp_table[i]->state) return i; } return -1;}/* * isdn_ppp_open */intisdn_ppp_open(int min, struct file *file){ int slot; struct ippp_struct *is; if (min < 0 || min > ISDN_MAX_CHANNELS) return -ENODEV; slot = isdn_ppp_get_slot(); if (slot < 0) { return -EBUSY; } is = file->private_data = ippp_table[slot]; printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state); /* compression stuff */ is->link_compressor = is->compressor = NULL; is->link_decompressor = is->decompressor = NULL; is->link_comp_stat = is->comp_stat = NULL; is->link_decomp_stat = is->decomp_stat = NULL; is->compflags = 0; is->reset = isdn_ppp_ccp_reset_alloc(is); is->lp = NULL; is->mp_seqno = 0; /* MP sequence number */ is->pppcfg = 0; /* ppp configuration */ is->mpppcfg = 0; /* mppp configuration */ is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ is->unit = -1; /* set, when we have our interface */ is->mru = 1524; /* MRU, default 1524 */ is->maxcid = 16; /* VJ: maxcid */ is->tk = current; init_waitqueue_head(&is->wq); is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ is->last = is->rq; is->minor = min;#ifdef CONFIG_ISDN_PPP_VJ /* * VJ header compression init */ is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */#endif#ifdef CONFIG_IPPP_FILTER is->pass_filter = NULL; is->active_filter = NULL;#endif is->state = IPPP_OPEN; return 0;}/* * release ippp device */voidisdn_ppp_release(int min, struct file *file){ int i; struct ippp_struct *is; if (min < 0 || min >= ISDN_MAX_CHANNELS) return; is = file->private_data; if (!is) { printk(KERN_ERR "%s: no file->private_data\n", __FUNCTION__); return; } if (is->debug & 0x1) printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp); if (is->lp) { /* a lp address says: this link is still up */ isdn_net_dev *p = is->lp->netdev; if (!p) { printk(KERN_ERR "%s: no lp->netdev\n", __FUNCTION__); return; } is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ /* * isdn_net_hangup() calls isdn_ppp_free() * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1 * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon() */ isdn_net_hangup(&p->dev); } for (i = 0; i < NUM_RCV_BUFFS; i++) { kfree(is->rq[i].buf); is->rq[i].buf = NULL; } is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ is->last = is->rq;#ifdef CONFIG_ISDN_PPP_VJ/* TODO: if this was the previous master: link the slcomp to the new master */ slhc_free(is->slcomp); is->slcomp = NULL;#endif#ifdef CONFIG_IPPP_FILTER kfree(is->pass_filter); is->pass_filter = NULL; kfree(is->active_filter); is->active_filter = NULL;#endif/* TODO: if this was the previous master: link the stuff to the new master */ if(is->comp_stat) is->compressor->free(is->comp_stat); if(is->link_comp_stat) is->link_compressor->free(is->link_comp_stat); if(is->link_decomp_stat) is->link_decompressor->free(is->link_decomp_stat); if(is->decomp_stat) is->decompressor->free(is->decomp_stat); is->compressor = is->link_compressor = NULL; is->decompressor = is->link_decompressor = NULL; is->comp_stat = is->link_comp_stat = NULL; is->decomp_stat = is->link_decomp_stat = NULL; /* Clean up if necessary */ if(is->reset) isdn_ppp_ccp_reset_free(is); /* this slot is ready for new connections */ is->state = 0;}/* * get_arg .. ioctl helper */static intget_arg(void __user *b, void *val, int len){ if (len <= 0) len = sizeof(void *); if (copy_from_user(val, b, len)) return -EFAULT; return 0;}/* * set arg .. ioctl helper */static intset_arg(void __user *b, void *val,int len){ if(len <= 0) len = sizeof(void *); if (copy_to_user(b, val, len)) return -EFAULT; return 0;}static int get_filter(void __user *arg, struct sock_filter **p){ struct sock_fprog uprog; struct sock_filter *code = NULL; int len, err; if (copy_from_user(&uprog, arg, sizeof(uprog))) return -EFAULT; if (!uprog.len) { *p = NULL; return 0; } /* uprog.len is unsigned short, so no overflow here */ len = uprog.len * sizeof(struct sock_filter); code = kmalloc(len, GFP_KERNEL); if (code == NULL) return -ENOMEM; if (copy_from_user(code, uprog.filter, len)) { kfree(code); return -EFAULT; } err = sk_chk_filter(code, uprog.len); if (err) { kfree(code); return err; } *p = code; return uprog.len;}/* * ippp device ioctl */intisdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg){ unsigned long val; int r,i,j; struct ippp_struct *is; isdn_net_local *lp; struct isdn_ppp_comp_data data; void __user *argp = (void __user *)arg; is = (struct ippp_struct *) file->private_data; lp = is->lp; if (is->debug & 0x1) printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state); if (!(is->state & IPPP_OPEN)) return -EINVAL; switch (cmd) { case PPPIOCBUNDLE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -