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

📄 hdlc_fr.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Generic HDLC support routines for Linux * Frame Relay support * * Copyright (C) 1999 - 2005 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 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 DTE mode: (exist,new,active) = FULL STATUS if "link reliable"		    = 0, 0, 0 if "link unreliable" No LMI: active = open and "link reliable" exist = new = not used CCITT LMI: ITU-T Q.933 Annex A ANSI LMI: ANSI T1.617 Annex D CISCO LMI: the original, aka "Gang of Four" LMI*/#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#undef DEBUG_LINK#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_CCITT_ANSI_LMI	0x08#define NLPID_CISCO_LMI		0x09#define LMI_CCITT_ANSI_DLCI	   0 /* LMI DLCI */#define LMI_CISCO_DLCI		1023#define LMI_CALLREF		0x00 /* Call Reference */#define LMI_ANSI_LOCKSHIFT	0x95 /* ANSI locking shift */#define LMI_ANSI_CISCO_REPTYPE	0x01 /* report type */#define LMI_CCITT_REPTYPE	0x51#define LMI_ANSI_CISCO_ALIVE	0x03 /* keep alive */#define LMI_CCITT_ALIVE		0x53#define LMI_ANSI_CISCO_PVCSTAT	0x07 /* PVC status */#define LMI_CCITT_PVCSTAT	0x57#define LMI_FULLREP		0x00 /* full report  */#define LMI_INTEGRITY		0x01 /* link integrity report */#define LMI_SINGLE		0x02 /* 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_CCITT_CISCO_LENGTH	  13 /* LMI frame lengths */#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(struct net_device *dev, u16 dlci){	hdlc_device *hdlc = dev_to_hdlc(dev);	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 = dev;	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 pvc_carrier(int on, pvc_device *pvc){	if (on) {		if (pvc->main)			if (!netif_carrier_ok(pvc->main))				netif_carrier_on(pvc->main);		if (pvc->ether)			if (!netif_carrier_ok(pvc->ether))				netif_carrier_on(pvc->ether);	} else {		if (pvc->main)			if (netif_carrier_ok(pvc->main))				netif_carrier_off(pvc->main);		if (pvc->ether)			if (netif_carrier_ok(pvc->ether))				netif_carrier_off(pvc->ether);	}}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 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(NLPID_CCITT_ANSI_LMI):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_CCITT_ANSI_LMI;		break;	case __constant_ntohs(NLPID_CISCO_LMI):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_CISCO_LMI;		break;	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(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 ((pvc->master->flags & IFF_UP) == 0)		return -EIO;  /* Master must be UP in order to activate PVC */	if (pvc->open_count++ == 0) {		hdlc_device *hdlc = dev_to_hdlc(pvc->master);		if (hdlc->state.fr.settings.lmi == LMI_NONE)			pvc->state.active = hdlc->carrier;		pvc_carrier(pvc->state.active, pvc);		hdlc->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) {		hdlc_device *hdlc = dev_to_hdlc(pvc->master);		if (hdlc->state.fr.settings.lmi == LMI_NONE)			pvc->state.active = 0;		if (hdlc->state.fr.settings.dce) {			hdlc->state.fr.dce_changed = 1;			pvc->state.active = 0;		}	}	return 0;}static 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, pvc->master->name, 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 netdev_priv(dev);}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 = 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",	       pvc->master->name,	       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(struct net_device *dev, int fullrep){	hdlc_device *hdlc = dev_to_hdlc(dev);	struct sk_buff *skb;	pvc_device *pvc = hdlc->state.fr.first_pvc;	int lmi = hdlc->state.fr.settings.lmi;	int dce = hdlc->state.fr.settings.dce;	int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;	int stat_len = (lmi == LMI_CISCO) ? 6 : 3;	u8 *data;	int i = 0;	if (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", dev->name);			return;		}	}	skb = dev_alloc_skb(len);	if (!skb) {		printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",		       dev->name);		return;	}	memset(skb->data, 0, len);	skb_reserve(skb, 4);	if (lmi == LMI_CISCO) {		skb->protocol = __constant_htons(NLPID_CISCO_LMI);		fr_hard_header(&skb, LMI_CISCO_DLCI);	} else {		skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI);		fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);	}	data = skb->tail;	data[i++] = LMI_CALLREF;	data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY;	if (lmi == LMI_ANSI)		data[i++] = LMI_ANSI_LOCKSHIFT;	data[i++] = lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :		LMI_ANSI_CISCO_REPTYPE;	data[i++] = LMI_REPT_LEN;	data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;	data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_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 (dce && fullrep) {		while (pvc) {			data[i++] = lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :				LMI_ANSI_CISCO_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_carrier(1, pvc);				pvc->state.active = 1;				fr_log_dlci_active(pvc);			}			if (lmi == LMI_CISCO) {				data[i] = pvc->dlci >> 8;				data[i + 1] = pvc->dlci & 0xFF;			} else {				data[i] = (pvc->dlci >> 4) & 0x3F;				data[i + 1] = ((pvc->dlci << 3) & 0x78) | 0x80;				data[i + 2] = 0x80;			}			if (pvc->state.new)				data[i + 2] |= 0x08;			else if (pvc->state.active)				data[i + 2] |= 0x02;			i += stat_len;			pvc = pvc->next;		}	}	skb_put(skb, i);	skb->priority = TC_PRIO_CONTROL;	skb->dev = dev;	skb->nh.raw = skb->data;	dev_queue_xmit(skb);}static void fr_set_link_state(int reliable, struct net_device *dev){	hdlc_device *hdlc = dev_to_hdlc(dev);	pvc_device *pvc = hdlc->state.fr.first_pvc;	hdlc->state.fr.reliable = reliable;	if (reliable) {#if 0		if (!netif_carrier_ok(dev))			netif_carrier_on(dev);#endif		hdlc->state.fr.n391cnt = 0; /* Request full status */		hdlc->state.fr.dce_changed = 1;		if (hdlc->state.fr.settings.lmi == LMI_NONE) {			while (pvc) {	/* Activate all PVCs */				pvc_carrier(1, pvc);				pvc->state.exist = pvc->state.active = 1;				pvc->state.new = 0;				pvc = pvc->next;			}		}	} else {#if 0		if (netif_carrier_ok(dev))			netif_carrier_off(dev);#endif		while (pvc) {		/* Deactivate all PVCs */			pvc_carrier(0, pvc);			pvc->state.exist = pvc->state.active = 0;			pvc->state.new = 0;			if (!hdlc->state.fr.settings.dce)				pvc->state.bandwidth = 0;			pvc = pvc->next;		}	}}static void fr_timer(unsigned long arg){	struct net_device *dev = (struct net_device *)arg;	hdlc_device *hdlc = dev_to_hdlc(dev);	int i, cnt = 0, reliable;	u32 list;	if (hdlc->state.fr.settings.dce) {		reliable = hdlc->state.fr.request &&			time_before(jiffies, hdlc->state.fr.last_poll +				    hdlc->state.fr.settings.t392 * HZ);		hdlc->state.fr.request = 0;	} 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", dev->name);			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) {		printk(KERN_INFO "%s: Link %sreliable\n", dev->name,		       reliable ? "" : "un");		fr_set_link_state(reliable, dev);	}	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(dev, hdlc->state.fr.n391cnt == 0);		hdlc->state.fr.last_poll = jiffies;		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);}

⌨️ 快捷键说明

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