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

📄 hdlc_fr.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Generic HDLC support routines for Linux * Frame Relay support * * Copyright (C) 1999 - 2006 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#undef DEBUG_PROTO#undef DEBUG_PVC#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;typedef struct pvc_device_struct {	struct net_device *frad;	struct net_device *main;	struct net_device *ether;	/* bridged Ethernet interface	*/	struct pvc_device_struct *next;	/* Sorted in ascending DLCI order */	int dlci;	int open_count;	struct {		unsigned int new: 1;		unsigned int active: 1;		unsigned int exist: 1;		unsigned int deleted: 1;		unsigned int fecn: 1;		unsigned int becn: 1;		unsigned int bandwidth;	/* Cisco LMI reporting only */	}state;}pvc_device;struct frad_state {	fr_proto settings;	pvc_device *first_pvc;	int dce_pvc_count;	struct timer_list timer;	unsigned long last_poll;	int reliable;	int dce_changed;	int request;	int fullrep_sent;	u32 last_errors; /* last errors bit list */	u8 n391cnt;	u8 txseq; /* TX sequence number */	u8 rxseq; /* RX sequence number */};static int fr_ioctl(struct net_device *dev, struct ifreq *ifr);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 struct frad_state * state(hdlc_device *hdlc){	return(struct frad_state *)(hdlc->state);}static __inline__ pvc_device* dev_to_pvc(struct net_device *dev){	return dev->priv;}static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci){	pvc_device *pvc = state(hdlc)->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 pvc_device* add_pvc(struct net_device *dev, u16 dlci){	hdlc_device *hdlc = dev_to_hdlc(dev);	pvc_device *pvc, **pvc_p = &state(hdlc)->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 = kzalloc(sizeof(pvc_device), GFP_ATOMIC);#ifdef DEBUG_PVC	printk(KERN_DEBUG "add_pvc: allocated pvc %p, frad %p\n", pvc, dev);#endif	if (!pvc)		return NULL;	pvc->dlci = dlci;	pvc->frad = 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 || pvc->ether;}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 = &state(hdlc)->first_pvc;	while (*pvc_p) {		if (!pvc_is_used(*pvc_p)) {			pvc_device *pvc = *pvc_p;#ifdef DEBUG_PVC			printk(KERN_DEBUG "freeing unused pvc: %p\n", pvc);#endif			*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_htons(NLPID_CCITT_ANSI_LMI):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_CCITT_ANSI_LMI;		break;	case __constant_htons(NLPID_CISCO_LMI):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_CISCO_LMI;		break;	case __constant_htons(ETH_P_IP):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_IP;		break;	case __constant_htons(ETH_P_IPV6):		head_len = 4;		skb_push(skb, head_len);		skb->data[3] = NLPID_IPV6;		break;	case __constant_htons(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;		*(__be16*)(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->frad->flags & IFF_UP) == 0)		return -EIO;  /* Frad must be UP in order to activate PVC */	if (pvc->open_count++ == 0) {		hdlc_device *hdlc = dev_to_hdlc(pvc->frad);		if (state(hdlc)->settings.lmi == LMI_NONE)			pvc->state.active = netif_carrier_ok(pvc->frad);		pvc_carrier(pvc->state.active, pvc);		state(hdlc)->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->frad);		if (state(hdlc)->settings.lmi == LMI_NONE)			pvc->state.active = 0;		if (state(hdlc)->settings.dce) {			state(hdlc)->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->frad->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 &dev_to_desc(dev)->stats;}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)) {

⌨️ 快捷键说明

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