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

📄 hdlc_fr.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb){	hdlc_device *hdlc = dev_to_hdlc(dev);	pvc_device *pvc;	u8 rxseq, txseq;	int lmi = hdlc->state.fr.settings.lmi;	int dce = hdlc->state.fr.settings.dce;	int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;	if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :			LMI_CCITT_CISCO_LENGTH)) {		printk(KERN_INFO "%s: Short LMI frame\n", dev->name);		return 1;	}	if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :			     NLPID_CCITT_ANSI_LMI)) {		printk(KERN_INFO "%s: Received non-LMI frame with LMI"		       " DLCI\n", dev->name);		return 1;	}	if (skb->data[4] != LMI_CALLREF) {		printk(KERN_INFO "%s: Invalid LMI Call reference (0x%02X)\n",		       dev->name, skb->data[4]);		return 1;	}	if (skb->data[5] != (dce ? LMI_STATUS_ENQUIRY : LMI_STATUS)) {		printk(KERN_INFO "%s: Invalid LMI Message type (0x%02X)\n",		       dev->name, skb->data[5]);		return 1;	}	if (lmi == LMI_ANSI) {		if (skb->data[6] != LMI_ANSI_LOCKSHIFT) {			printk(KERN_INFO "%s: Not ANSI locking shift in LMI"			       " message (0x%02X)\n", dev->name, skb->data[6]);			return 1;		}		i = 7;	} else		i = 6;	if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :			     LMI_ANSI_CISCO_REPTYPE)) {		printk(KERN_INFO "%s: Not an LMI Report type IE (0x%02X)\n",		       dev->name, skb->data[i]);		return 1;	}	if (skb->data[++i] != LMI_REPT_LEN) {		printk(KERN_INFO "%s: Invalid LMI Report type IE length"		       " (%u)\n", dev->name, skb->data[i]);		return 1;	}	reptype = skb->data[++i];	if (reptype != LMI_INTEGRITY && reptype != LMI_FULLREP) {		printk(KERN_INFO "%s: Unsupported LMI Report type (0x%02X)\n",		       dev->name, reptype);		return 1;	}	if (skb->data[++i] != (lmi == LMI_CCITT ? LMI_CCITT_ALIVE :			       LMI_ANSI_CISCO_ALIVE)) {		printk(KERN_INFO "%s: Not an LMI Link integrity verification"		       " IE (0x%02X)\n", dev->name, skb->data[i]);		return 1;	}	if (skb->data[++i] != LMI_INTEG_LEN) {		printk(KERN_INFO "%s: Invalid LMI Link integrity verification"		       " IE length (%u)\n", dev->name, skb->data[i]);		return 1;	}	i++;	hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */	rxseq = skb->data[i++];	/* Should confirm our sequence */	txseq = hdlc->state.fr.txseq;	if (dce)		hdlc->state.fr.last_poll = jiffies;	error = 0;	if (!hdlc->state.fr.reliable)		error = 1;	if (rxseq == 0 || rxseq != txseq) {		hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */		error = 1;	}	if (dce) {		if (hdlc->state.fr.fullrep_sent && !error) {/* Stop sending full report - the last one has been confirmed by DTE */			hdlc->state.fr.fullrep_sent = 0;			pvc = hdlc->state.fr.first_pvc;			while (pvc) {				if (pvc->state.new) {					pvc->state.new = 0;/* Tell DTE that new PVC is now active */					hdlc->state.fr.dce_changed = 1;				}				pvc = pvc->next;			}		}		if (hdlc->state.fr.dce_changed) {			reptype = LMI_FULLREP;			hdlc->state.fr.fullrep_sent = 1;			hdlc->state.fr.dce_changed = 0;		}		hdlc->state.fr.request = 1; /* got request */		fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);		return 0;	}	/* DTE */	hdlc->state.fr.request = 0; /* got response, no request pending */	if (error)		return 0;	if (reptype != LMI_FULLREP)		return 0;	pvc = hdlc->state.fr.first_pvc;	while (pvc) {		pvc->state.deleted = 1;		pvc = pvc->next;	}	no_ram = 0;	while (skb->len >= i + 2 + stat_len) {		u16 dlci;		u32 bw;		unsigned int active, new;		if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :				       LMI_ANSI_CISCO_PVCSTAT)) {			printk(KERN_INFO "%s: Not an LMI PVC status IE"			       " (0x%02X)\n", dev->name, skb->data[i]);			return 1;		}		if (skb->data[++i] != stat_len) {			printk(KERN_INFO "%s: Invalid LMI PVC status IE length"			       " (%u)\n", dev->name, skb->data[i]);			return 1;		}		i++;		new = !! (skb->data[i + 2] & 0x08);		active = !! (skb->data[i + 2] & 0x02);		if (lmi == LMI_CISCO) {			dlci = (skb->data[i] << 8) | skb->data[i + 1];			bw = (skb->data[i + 3] << 16) |				(skb->data[i + 4] << 8) |				(skb->data[i + 5]);		} else {			dlci = ((skb->data[i] & 0x3F) << 4) |				((skb->data[i + 1] & 0x78) >> 3);			bw = 0;		}		pvc = add_pvc(dev, dlci);		if (!pvc && !no_ram) {			printk(KERN_WARNING			       "%s: Memory squeeze on fr_lmi_recv()\n",			       dev->name);			no_ram = 1;		}		if (pvc) {			pvc->state.exist = 1;			pvc->state.deleted = 0;			if (active != pvc->state.active ||			    new != pvc->state.new ||			    bw != pvc->state.bandwidth ||			    !pvc->state.exist) {				pvc->state.new = new;				pvc->state.active = active;				pvc->state.bandwidth = bw;				pvc_carrier(active, pvc);				fr_log_dlci_active(pvc);			}		}		i += stat_len;	}	pvc = hdlc->state.fr.first_pvc;	while (pvc) {		if (pvc->state.deleted && pvc->state.exist) {			pvc_carrier(0, pvc);			pvc->state.active = pvc->state.new = 0;			pvc->state.exist = 0;			pvc->state.bandwidth = 0;			fr_log_dlci_active(pvc);		}		pvc = pvc->next;	}	/* Next full report after N391 polls */	hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;	return 0;}static int fr_rx(struct sk_buff *skb){	struct net_device *ndev = skb->dev;	hdlc_device *hdlc = dev_to_hdlc(ndev);	fr_hdr *fh = (fr_hdr*)skb->data;	u8 *data = skb->data;	u16 dlci;	pvc_device *pvc;	struct net_device *dev = NULL;	if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI)		goto rx_error;	dlci = q922_to_dlci(skb->data);	if ((dlci == LMI_CCITT_ANSI_DLCI &&	     (hdlc->state.fr.settings.lmi == LMI_ANSI ||	      hdlc->state.fr.settings.lmi == LMI_CCITT)) ||	    (dlci == LMI_CISCO_DLCI &&	     hdlc->state.fr.settings.lmi == LMI_CISCO)) {		if (fr_lmi_recv(ndev, skb))			goto rx_error;		dev_kfree_skb_any(skb);		return NET_RX_SUCCESS;	}	pvc = find_pvc(hdlc, dlci);	if (!pvc) {#ifdef DEBUG_PKT		printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",		       ndev->name, dlci);#endif		dev_kfree_skb_any(skb);		return NET_RX_DROP;	}	if (pvc->state.fecn != fh->fecn) {#ifdef DEBUG_ECN		printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", ndev->name,		       dlci, fh->fecn ? "N" : "FF");#endif		pvc->state.fecn ^= 1;	}	if (pvc->state.becn != fh->becn) {#ifdef DEBUG_ECN		printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", ndev->name,		       dlci, fh->becn ? "N" : "FF");#endif		pvc->state.becn ^= 1;	}	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {		hdlc->stats.rx_dropped++;		return NET_RX_DROP;	}	if (data[3] == NLPID_IP) {		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */		dev = pvc->main;		skb->protocol = htons(ETH_P_IP);	} else if (data[3] == NLPID_IPV6) {		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */		dev = pvc->main;		skb->protocol = htons(ETH_P_IPV6);	} else if (skb->len > 10 && data[3] == FR_PAD &&		   data[4] == NLPID_SNAP && data[5] == FR_PAD) {		u16 oui = ntohs(*(u16*)(data + 6));		u16 pid = ntohs(*(u16*)(data + 8));		skb_pull(skb, 10);		switch ((((u32)oui) << 16) | pid) {		case ETH_P_ARP: /* routed frame with SNAP */		case ETH_P_IPX:		case ETH_P_IP:	/* a long variant */		case ETH_P_IPV6:			dev = pvc->main;			skb->protocol = htons(pid);			break;		case 0x80C20007: /* bridged Ethernet frame */			if ((dev = pvc->ether) != NULL)				skb->protocol = eth_type_trans(skb, dev);			break;		default:			printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "			       "PID=%x\n", ndev->name, oui, pid);			dev_kfree_skb_any(skb);			return NET_RX_DROP;		}	} else {		printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "		       "length = %i\n", ndev->name, data[3], skb->len);		dev_kfree_skb_any(skb);		return NET_RX_DROP;	}	if (dev) {		struct net_device_stats *stats = pvc_get_stats(dev);		stats->rx_packets++; /* PVC traffic */		stats->rx_bytes += skb->len;		if (pvc->state.becn)			stats->rx_compressed++;		skb->dev = dev;		netif_rx(skb);		return NET_RX_SUCCESS;	} else {		dev_kfree_skb_any(skb);		return NET_RX_DROP;	} rx_error:	hdlc->stats.rx_errors++; /* Mark error */	dev_kfree_skb_any(skb);	return NET_RX_DROP;}static void fr_start(struct net_device *dev){	hdlc_device *hdlc = dev_to_hdlc(dev);#ifdef DEBUG_LINK	printk(KERN_DEBUG "fr_start\n");#endif	if (hdlc->state.fr.settings.lmi != LMI_NONE) {		hdlc->state.fr.reliable = 0;		hdlc->state.fr.dce_changed = 1;		hdlc->state.fr.request = 0;		hdlc->state.fr.fullrep_sent = 0;		hdlc->state.fr.last_errors = 0xFFFFFFFF;		hdlc->state.fr.n391cnt = 0;		hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;		init_timer(&hdlc->state.fr.timer);		/* First poll after 1 s */		hdlc->state.fr.timer.expires = jiffies + HZ;		hdlc->state.fr.timer.function = fr_timer;		hdlc->state.fr.timer.data = (unsigned long)dev;		add_timer(&hdlc->state.fr.timer);	} else		fr_set_link_state(1, dev);}static void fr_stop(struct net_device *dev){	hdlc_device *hdlc = dev_to_hdlc(dev);#ifdef DEBUG_LINK	printk(KERN_DEBUG "fr_stop\n");#endif	if (hdlc->state.fr.settings.lmi != LMI_NONE)		del_timer_sync(&hdlc->state.fr.timer);	fr_set_link_state(0, dev);}static void fr_close(struct net_device *dev){	hdlc_device *hdlc = dev_to_hdlc(dev);	pvc_device *pvc = hdlc->state.fr.first_pvc;	while (pvc) {		/* Shutdown all PVCs for this FRAD */		if (pvc->main)			dev_close(pvc->main);		if (pvc->ether)			dev_close(pvc->ether);		pvc = pvc->next;	}}static void dlci_setup(struct net_device *dev){	dev->type = ARPHRD_DLCI;	dev->flags = IFF_POINTOPOINT;	dev->hard_header_len = 10;	dev->addr_len = 2;}static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type){	hdlc_device *hdlc = dev_to_hdlc(master);	pvc_device *pvc = NULL;	struct net_device *dev;	int result, used;	char * prefix = "pvc%d";	if (type == ARPHRD_ETHER)		prefix = "pvceth%d";	if ((pvc = add_pvc(master, dlci)) == NULL) {		printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",		       master->name);		return -ENOBUFS;	}	if (*get_dev_p(pvc, type))		return -EEXIST;	used = pvc_is_used(pvc);	if (type == ARPHRD_ETHER)		dev = alloc_netdev(sizeof(struct net_device_stats),				   "pvceth%d", ether_setup);	else		dev = alloc_netdev(sizeof(struct net_device_stats),				   "pvc%d", dlci_setup);	if (!dev) {		printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",		       master->name);		delete_unused_pvcs(hdlc);		return -ENOBUFS;	}	if (type == ARPHRD_ETHER) {		memcpy(dev->dev_addr, "\x00\x01", 2);                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);	} else {		*(u16*)dev->dev_addr = htons(dlci);		dlci_to_q922(dev->broadcast, dlci);	}	dev->hard_start_xmit = pvc_xmit;	dev->get_stats = pvc_get_stats;	dev->open = pvc_open;	dev->stop = pvc_close;	dev->do_ioctl = pvc_ioctl;	dev->change_mtu = pvc_change_mtu;	dev->mtu = HDLC_MAX_MTU;	dev->tx_queue_len = 0;	dev->priv = pvc;	result = dev_alloc_name(dev, dev->name);	if (result < 0) {		free_netdev(dev);		delete_unused_pvcs(hdlc);		return result;	}	if (register_netdevice(dev) != 0) {		free_netdev(dev);		delete_unused_pvcs(hdlc);		return -EIO;	}	dev->destructor = free_netdev;	*get_dev_p(pvc, type) = dev;	if (!used) {		hdlc->state.fr.dce_changed = 1;		hdlc->state.fr.dce_pvc_count++;	}	return 0;}static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type){	pvc_device *pvc;	struct net_device *dev;	if ((pvc = find_pvc(hdlc, dlci)) == NULL)		return -ENOENT;	if ((dev = *get_dev_p(pvc, type)) == NULL)		return -ENOENT;	if (dev->flags & IFF_UP)		return -EBUSY;		/* PVC in use */	unregister_netdevice(dev); /* the destructor will free_netdev(dev) */	*get_dev_p(pvc, type) = NULL;	if (!pvc_is_used(pvc)) {		hdlc->state.fr.dce_pvc_count--;		hdlc->state.fr.dce_changed = 1;	}	delete_unused_pvcs(hdlc);	return 0;}static void fr_destroy(hdlc_device *hdlc){	pvc_device *pvc;	pvc = hdlc->state.fr.first_pvc;	hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */	hdlc->state.fr.dce_pvc_count = 0;	hdlc->state.fr.dce_changed = 1;	while (pvc) {		pvc_device *next = pvc->next;		/* destructors will free_netdev() main and ether */		if (pvc->main)			unregister_netdevice(pvc->main);		if (pvc->ether)			unregister_netdevice(pvc->ether);		kfree(pvc);		pvc = next;	}}int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr){	fr_proto __user *fr_s = ifr->ifr_settings.ifs_ifsu.fr;	const size_t size = sizeof(fr_proto);	fr_proto new_settings;	hdlc_device *hdlc = dev_to_hdlc(dev);	fr_proto_pvc pvc;	int result;	switch (ifr->ifr_settings.type) {	case IF_GET_PROTO:		ifr->ifr_settings.type = IF_PROTO_FR;		if (ifr->ifr_settings.size < size) {			ifr->ifr_settings.size = size; /* data size wanted */			return -ENOBUFS;		}		if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))			return -EFAULT;		return 0;	case IF_PROTO_FR:		if(!capable(CAP_NET_ADMIN))			return -EPERM;		if(dev->flags & IFF_UP)			return -EBUSY;		if (copy_from_user(&new_settings, fr_s, size))			return -EFAULT;		if (new_settings.lmi == LMI_DEFAULT)			new_settings.lmi = LMI_ANSI;		if ((new_settings.lmi != LMI_NONE &&		     new_settings.lmi != LMI_ANSI &&		     new_settings.lmi != LMI_CCITT &&		     new_settings.lmi != LMI_CISCO) ||		    new_settings.t391 < 1 ||		    new_settings.t392 < 2 ||		    new_settings.n391 < 1 ||		    new_settings.n392 < 1 ||		    new_settings.n393 < new_settings.n392 ||		    new_settings.n393 > 32 ||		    (new_settings.dce != 0 &&		     new_settings.dce != 1))			return -EINVAL;		result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);		if (result)			return result;		if (hdlc->proto.id != IF_PROTO_FR) {			hdlc_proto_detach(hdlc);			hdlc->state.fr.first_pvc = NULL;			hdlc->state.fr.dce_pvc_count = 0;		}		memcpy(&hdlc->state.fr.settings, &new_settings, size);		memset(&hdlc->proto, 0, sizeof(hdlc->proto));		hdlc->proto.close = fr_close;		hdlc->proto.start = fr_start;		hdlc->proto.stop = fr_stop;		hdlc->proto.detach = fr_destroy;		hdlc->proto.netif_rx = fr_rx;		hdlc->proto.id = IF_PROTO_FR;		dev->hard_start_xmit = hdlc->xmit;		dev->hard_header = NULL;		dev->type = ARPHRD_FRAD;		dev->flags = IFF_POINTOPOINT | IFF_NOARP;		dev->addr_len = 0;		return 0;	case IF_PROTO_FR_ADD_PVC:	case IF_PROTO_FR_DEL_PVC:	case IF_PROTO_FR_ADD_ETH_PVC:	case IF_PROTO_FR_DEL_ETH_PVC:		if(!capable(CAP_NET_ADMIN))			return -EPERM;		if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc,				   sizeof(fr_proto_pvc)))			return -EFAULT;		if (pvc.dlci <= 0 || pvc.dlci >= 1024)			return -EINVAL;	/* Only 10 bits, DLCI 0 reserved */		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC ||		    ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC)			result = ARPHRD_ETHER; /* bridged Ethernet device */		else			result = ARPHRD_DLCI;		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC ||		    ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC)			return fr_add_pvc(dev, pvc.dlci, result);		else			return fr_del_pvc(hdlc, pvc.dlci, result);	}	return -EINVAL;}

⌨️ 快捷键说明

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