⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hdlc_fr.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Generic HDLC support routines for Linux * Frame Relay support * * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * Theory of PVC state in DCE mode: (exist,new) -> 0,0 when "PVC create" or if "link unreliable"         0,x -> 1,1 if "link reliable" when sending FULL STATUS         1,1 -> 1,0 if received FULL STATUS ACK (active)    -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create"             -> 1 when "PVC up" and (exist,new) = 1,0*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/errno.h>#include <linux/if_arp.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/pkt_sched.h>#include <linux/random.h>#include <linux/inetdevice.h>#include <linux/lapb.h>#include <linux/rtnetlink.h>#include <linux/etherdevice.h>#include <linux/hdlc.h>#undef DEBUG_PKT#undef DEBUG_ECN#define MAXLEN_LMISTAT  20	/* max size of status enquiry frame */#define PVC_STATE_NEW	 0x01#define PVC_STATE_ACTIVE 0x02#define PVC_STATE_FECN	 0x08 /* FECN condition */#define PVC_STATE_BECN	 0x10 /* BECN condition */#define FR_UI		 0x03#define FR_PAD		 0x00#define NLPID_IP	 0xCC#define NLPID_IPV6	 0x8E#define NLPID_SNAP	 0x80#define NLPID_PAD	 0x00#define NLPID_Q933	 0x08#define LMI_DLCI                   0 /* LMI DLCI */#define LMI_PROTO               0x08#define LMI_CALLREF             0x00 /* Call Reference */#define LMI_ANSI_LOCKSHIFT      0x95 /* ANSI lockshift */#define LMI_REPTYPE                1 /* report type */#define LMI_CCITT_REPTYPE       0x51#define LMI_ALIVE                  3 /* keep alive */#define LMI_CCITT_ALIVE         0x53#define LMI_PVCSTAT                7 /* pvc status */#define LMI_CCITT_PVCSTAT       0x57#define LMI_FULLREP                0 /* full report  */#define LMI_INTEGRITY              1 /* link integrity report */#define LMI_SINGLE                 2 /* single pvc report */#define LMI_STATUS_ENQUIRY      0x75#define LMI_STATUS              0x7D /* reply */#define LMI_REPT_LEN               1 /* report type element length */#define LMI_INTEG_LEN              2 /* link integrity element length */#define LMI_LENGTH                13 /* standard LMI frame length */#define LMI_ANSI_LENGTH           14typedef struct {#if defined(__LITTLE_ENDIAN_BITFIELD)	unsigned ea1:	1;	unsigned cr:	1;	unsigned dlcih:	6;  	unsigned ea2:	1;	unsigned de:	1;	unsigned becn:	1;	unsigned fecn:	1;	unsigned dlcil:	4;#else	unsigned dlcih:	6;	unsigned cr:	1;	unsigned ea1:	1;	unsigned dlcil:	4;	unsigned fecn:	1;	unsigned becn:	1;	unsigned de:	1;	unsigned ea2:	1;#endif}__attribute__ ((packed)) fr_hdr;static inline u16 q922_to_dlci(u8 *hdr){	return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);}static inline void dlci_to_q922(u8 *hdr, u16 dlci){	hdr[0] = (dlci >> 2) & 0xFC;	hdr[1] = ((dlci << 4) & 0xF0) | 0x01;}static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci){	pvc_device *pvc = hdlc->state.fr.first_pvc;	while (pvc) {		if (pvc->dlci == dlci)			return pvc;		if (pvc->dlci > dlci)			return NULL; /* the listed is sorted */		pvc = pvc->next;	}	return NULL;}static inline pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci){	pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;	while (*pvc_p) {		if ((*pvc_p)->dlci == dlci)			return *pvc_p;		if ((*pvc_p)->dlci > dlci)			break;	/* the list is sorted */		pvc_p = &(*pvc_p)->next;	}	pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);	if (!pvc)		return NULL;	memset(pvc, 0, sizeof(pvc_device));	pvc->dlci = dlci;	pvc->master = hdlc;	pvc->next = *pvc_p;	/* Put it in the chain */	*pvc_p = pvc;	return pvc;}static inline int pvc_is_used(pvc_device *pvc){	return pvc->main != NULL || pvc->ether != NULL;}static inline void delete_unused_pvcs(hdlc_device *hdlc){	pvc_device **pvc_p = &hdlc->state.fr.first_pvc;	while (*pvc_p) {		if (!pvc_is_used(*pvc_p)) {			pvc_device *pvc = *pvc_p;			*pvc_p = pvc->next;			kfree(pvc);			continue;		}		pvc_p = &(*pvc_p)->next;	}}static inline struct net_device** get_dev_p(pvc_device *pvc, int type){	if (type == ARPHRD_ETHER)		return &pvc->ether;	else		return &pvc->main;}static inline u16 status_to_dlci(u8 *status, int *active, int *new){	*new = (status[2] & 0x08) ? 1 : 0;	*active = (status[2] & 0x02) ? 1 : 0;	return ((status[0] & 0x3F) << 4) | ((status[1] & 0x78) >> 3);}static inline void dlci_to_status(u16 dlci, u8 *status, int active, int new){	status[0] = (dlci >> 4) & 0x3F;	status[1] = ((dlci << 3) & 0x78) | 0x80;	status[2] = 0x80;	if (new)		status[2] |= 0x08;	else if (active)		status[2] |= 0x02;}static int fr_hard_header(struct sk_buff **skb_p, u16 dlci){	u16 head_len;	struct sk_buff *skb = *skb_p;	switch (skb->protocol) {	case __constant_ntohs(ETH_P_IP):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_IP;		break;	case __constant_ntohs(ETH_P_IPV6):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_IPV6;		break;	case __constant_ntohs(LMI_PROTO):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = LMI_PROTO;		break;	case __constant_ntohs(ETH_P_802_3):		head_len = 10;		if (skb_headroom(skb) < head_len) {			struct sk_buff *skb2 = skb_realloc_headroom(skb,								    head_len);			if (!skb2)				return -ENOBUFS;			dev_kfree_skb(skb);			skb = *skb_p = skb2;		}		skb_push(skb, head_len);		skb->data[3] = FR_PAD;		skb->data[4] = NLPID_SNAP;		skb->data[5] = FR_PAD;		skb->data[6] = 0x80;		skb->data[7] = 0xC2;		skb->data[8] = 0x00;		skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */		break;	default:		head_len = 10;		skb_push(skb, head_len);		skb->data[3] = FR_PAD;		skb->data[4] = NLPID_SNAP;		skb->data[5] = FR_PAD;		skb->data[6] = FR_PAD;		skb->data[7] = FR_PAD;		*(u16*)(skb->data + 8) = skb->protocol;	}	dlci_to_q922(skb->data, dlci);	skb->data[2] = FR_UI;	return 0;}static int pvc_open(struct net_device *dev){	pvc_device *pvc = dev_to_pvc(dev);	if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)		return -EIO;  /* Master must be UP in order to activate PVC */	if (pvc->open_count++ == 0) {		if (pvc->master->state.fr.settings.lmi == LMI_NONE)			pvc->state.active = 1;		pvc->master->state.fr.dce_changed = 1;	}	return 0;}static int pvc_close(struct net_device *dev){	pvc_device *pvc = dev_to_pvc(dev);	if (--pvc->open_count == 0) {		if (pvc->master->state.fr.settings.lmi == LMI_NONE)			pvc->state.active = 0;		if (pvc->master->state.fr.settings.dce) {			pvc->master->state.fr.dce_changed = 1;			pvc->state.active = 0;		}	}	return 0;}int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){	pvc_device *pvc = dev_to_pvc(dev);	fr_proto_pvc_info info;	if (ifr->ifr_settings.type == IF_GET_PROTO) {		if (dev->type == ARPHRD_ETHER)			ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC;		else			ifr->ifr_settings.type = IF_PROTO_FR_PVC;		if (ifr->ifr_settings.size < sizeof(info)) {			/* data size wanted */			ifr->ifr_settings.size = sizeof(info);			return -ENOBUFS;		}		info.dlci = pvc->dlci;		memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ);		if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,				 &info, sizeof(info)))			return -EFAULT;		return 0;	}	return -EINVAL;}static inline struct net_device_stats *pvc_get_stats(struct net_device *dev){	return (struct net_device_stats *)		((char *)dev + sizeof(struct net_device));}static int pvc_xmit(struct sk_buff *skb, struct net_device *dev){	pvc_device *pvc = dev_to_pvc(dev);	struct net_device_stats *stats = pvc_get_stats(dev);	if (pvc->state.active) {		if (dev->type == ARPHRD_ETHER) {			int pad = ETH_ZLEN - skb->len;			if (pad > 0) { /* Pad the frame with zeros */				int len = skb->len;				if (skb_tailroom(skb) < pad)					if (pskb_expand_head(skb, 0, pad,							     GFP_ATOMIC)) {						stats->tx_dropped++;						dev_kfree_skb(skb);						return 0;					}				skb_put(skb, pad);				memset(skb->data + len, 0, pad);			}			skb->protocol = __constant_htons(ETH_P_802_3);		}		if (!fr_hard_header(&skb, pvc->dlci)) {			stats->tx_bytes += skb->len;			stats->tx_packets++;			if (pvc->state.fecn) /* TX Congestion counter */				stats->tx_compressed++;			skb->dev = hdlc_to_dev(pvc->master);			dev_queue_xmit(skb);			return 0;		}	}	stats->tx_dropped++;	dev_kfree_skb(skb);	return 0;}static int pvc_change_mtu(struct net_device *dev, int new_mtu){	if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))		return -EINVAL;	dev->mtu = new_mtu;	return 0;}static inline void fr_log_dlci_active(pvc_device *pvc){	printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",	       hdlc_to_name(pvc->master),	       pvc->dlci,	       pvc->main ? pvc->main->name : "",	       pvc->main && pvc->ether ? " " : "",	       pvc->ether ? pvc->ether->name : "",	       pvc->state.new ? " new" : "",	       !pvc->state.exist ? "deleted" :	       pvc->state.active ? "active" : "inactive");}static inline u8 fr_lmi_nextseq(u8 x){	x++;	return x ? x : 1;}static void fr_lmi_send(hdlc_device *hdlc, int fullrep){	struct sk_buff *skb;	pvc_device *pvc = hdlc->state.fr.first_pvc;	int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH		: LMI_LENGTH;	int stat_len = 3;	u8 *data;	int i = 0;	if (hdlc->state.fr.settings.dce && fullrep) {		len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);		if (len > HDLC_MAX_MRU) {			printk(KERN_WARNING "%s: Too many PVCs while sending "			       "LMI full report\n", hdlc_to_name(hdlc));			return;		}	}	skb = dev_alloc_skb(len);	if (!skb) {		printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",		       hdlc_to_name(hdlc));		return;	}	memset(skb->data, 0, len);	skb_reserve(skb, 4);	skb->protocol = __constant_htons(LMI_PROTO);	fr_hard_header(&skb, LMI_DLCI);	data = skb->tail;	data[i++] = LMI_CALLREF;	data[i++] = hdlc->state.fr.settings.dce		? LMI_STATUS : LMI_STATUS_ENQUIRY;	if (hdlc->state.fr.settings.lmi == LMI_ANSI)		data[i++] = LMI_ANSI_LOCKSHIFT;	data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)		? LMI_CCITT_REPTYPE : LMI_REPTYPE;	data[i++] = LMI_REPT_LEN;	data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;	data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)		? LMI_CCITT_ALIVE : LMI_ALIVE;	data[i++] = LMI_INTEG_LEN;	data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);	data[i++] = hdlc->state.fr.rxseq;	if (hdlc->state.fr.settings.dce && fullrep) {		while (pvc) {			data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)				? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;			data[i++] = stat_len;			/* LMI start/restart */			if (hdlc->state.fr.reliable && !pvc->state.exist) {				pvc->state.exist = pvc->state.new = 1;				fr_log_dlci_active(pvc);			}			/* ifconfig PVC up */			if (pvc->open_count && !pvc->state.active &&			    pvc->state.exist && !pvc->state.new) {				pvc->state.active = 1;				fr_log_dlci_active(pvc);			}			dlci_to_status(pvc->dlci, data + i,				       pvc->state.active, pvc->state.new);			i += stat_len;			pvc = pvc->next;		}	}	skb_put(skb, i);	skb->priority = TC_PRIO_CONTROL;	skb->dev = hdlc_to_dev(hdlc);	skb->nh.raw = skb->data;	dev_queue_xmit(skb);}static void fr_timer(unsigned long arg){	hdlc_device *hdlc = (hdlc_device*)arg;	int i, cnt = 0, reliable;	u32 list;	if (hdlc->state.fr.settings.dce)		reliable = (jiffies - hdlc->state.fr.last_poll <			    hdlc->state.fr.settings.t392 * HZ);	else {		hdlc->state.fr.last_errors <<= 1; /* Shift the list */		if (hdlc->state.fr.request) {			if (hdlc->state.fr.reliable)				printk(KERN_INFO "%s: No LMI status reply "				       "received\n", hdlc_to_name(hdlc));			hdlc->state.fr.last_errors |= 1;		}		list = hdlc->state.fr.last_errors;		for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)			cnt += (list & 1);	/* errors count */		reliable = (cnt < hdlc->state.fr.settings.n392);	}	if (hdlc->state.fr.reliable != reliable) {		pvc_device *pvc = hdlc->state.fr.first_pvc;		hdlc->state.fr.reliable = reliable;		printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),		       reliable ? "" : "un");		if (reliable) {			hdlc->state.fr.n391cnt = 0; /* Request full status */			hdlc->state.fr.dce_changed = 1;		} else {			while (pvc) {	/* Deactivate all PVCs */				pvc->state.exist = 0;				pvc->state.active = pvc->state.new = 0;				pvc = pvc->next;			}		}	}	if (hdlc->state.fr.settings.dce)		hdlc->state.fr.timer.expires = jiffies +			hdlc->state.fr.settings.t392 * HZ;	else {		if (hdlc->state.fr.n391cnt)			hdlc->state.fr.n391cnt--;		fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0);		hdlc->state.fr.request = 1;		hdlc->state.fr.timer.expires = jiffies +			hdlc->state.fr.settings.t391 * HZ;	}	hdlc->state.fr.timer.function = fr_timer;	hdlc->state.fr.timer.data = arg;	add_timer(&hdlc->state.fr.timer);}static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -