📄 isdn_ppp.c
字号:
/* $Id: isdn_ppp.c,v 1.85 2000/11/25 17:00:59 kai Exp $ * * Linux ISDN subsystem, functions for synchronous PPP (linklevel). * * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <linux/config.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/version.h>#include <linux/poll.h>#include <linux/isdn.h>#include <linux/ppp-comp.h>#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.85 $";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){ unsigned long flags; struct ippp_struct *is; if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) return 0; save_flags(flags); cli();#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 */ 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 ?? */ restore_flags(flags); return 0;}/* * bind isdn_net_local <=> ippp-device */intisdn_ppp_bind(isdn_net_local * lp){ int i; int unit = 0; long flags; struct ippp_struct *is; save_flags(flags); cli(); 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) { restore_flags(flags); printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n"); return -1; } 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); return -1; } 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 if (isdn_ppp_mp_init(lp, NULL) < 0) return -ENOMEM;#endif /* CONFIG_ISDN_MPP */ restore_flags(flags); return lp->ppp_slot;}/* * 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) 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) 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 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->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; 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++) { if (is->rq[i].buf) { 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/* TODO: if this was the previous master: link the 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 *b, void *val, int len){ if (len <= 0) len = sizeof(void *); if (copy_from_user((void *) val, b, len)) return -EFAULT; return 0;}/* * set arg .. ioctl helper */static intset_arg(void *b, void *val,int len){ if(len <= 0) len = sizeof(void *); if (copy_to_user(b, (void *) val, len)) return -EFAULT; return 0;}/* * 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; 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:#ifdef CONFIG_ISDN_MPP if (!(is->state & IPPP_CONNECT)) return -EINVAL; if ((r = get_arg((void *) arg, &val, sizeof(val) ))) return r; printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", (int) min, (int) is->unit, (int) val); return isdn_ppp_bundle(is, val);#else return -1;#endif break; case PPPIOCGUNIT: /* get ppp/isdn unit number */ if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) ))) return r; break; case PPPIOCGIFNAME: if(!lp) return -EINVAL; if ((r = set_arg((void *) arg, lp->name, strlen(lp->name)))) return r; break; case PPPIOCGMPFLAGS: /* get configuration flags */ if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) ))) return r; break; case PPPIOCSMPFLAGS: /* set configuration flags */ if ((r = get_arg((void *) arg, &val, sizeof(val) ))) return r; is->mpppcfg = val; break; case PPPIOCGFLAGS: /* get configuration flags */ if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) ))) return r; break; case PPPIOCSFLAGS: /* set configuration flags */ if ((r = get_arg((void *) arg, &val, sizeof(val) ))) { return r; } if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) { if (lp) { /* OK .. we are ready to send buffers */ netif_wake_queue(&lp->netdev->dev); } } is->pppcfg = val; break; case PPPIOCGIDLE: /* get idle time information */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -