📄 hdlc_fr.c
字号:
/* Next full report after N391 polls */ state(hdlc)->n391cnt = state(hdlc)->settings.n391; return 0;}static int fr_rx(struct sk_buff *skb){ struct net_device *frad = skb->dev; hdlc_device *hdlc = dev_to_hdlc(frad); 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 && (state(hdlc)->settings.lmi == LMI_ANSI || state(hdlc)->settings.lmi == LMI_CCITT)) || (dlci == LMI_CISCO_DLCI && state(hdlc)->settings.lmi == LMI_CISCO)) { if (fr_lmi_recv(frad, 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", frad->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", frad->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", frad->name, dlci, fh->becn ? "N" : "FF");#endif pvc->state.becn ^= 1; } if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { dev_to_desc(frad)->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(*(__be16*)(data + 6)); u16 pid = ntohs(*(__be16*)(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", frad->name, oui, pid); dev_kfree_skb_any(skb); return NET_RX_DROP; } } else { printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x " "length = %i\n", frad->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++; netif_rx(skb); return NET_RX_SUCCESS; } else { dev_kfree_skb_any(skb); return NET_RX_DROP; } rx_error: dev_to_desc(frad)->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 (state(hdlc)->settings.lmi != LMI_NONE) { state(hdlc)->reliable = 0; state(hdlc)->dce_changed = 1; state(hdlc)->request = 0; state(hdlc)->fullrep_sent = 0; state(hdlc)->last_errors = 0xFFFFFFFF; state(hdlc)->n391cnt = 0; state(hdlc)->txseq = state(hdlc)->rxseq = 0; init_timer(&state(hdlc)->timer); /* First poll after 1 s */ state(hdlc)->timer.expires = jiffies + HZ; state(hdlc)->timer.function = fr_timer; state(hdlc)->timer.data = (unsigned long)dev; add_timer(&state(hdlc)->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 (state(hdlc)->settings.lmi != LMI_NONE) del_timer_sync(&state(hdlc)->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 = state(hdlc)->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 pvc_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 *frad, unsigned int dlci, int type){ hdlc_device *hdlc = dev_to_hdlc(frad); 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(frad, dlci)) == NULL) { printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n", frad->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", pvc_setup); if (!dev) { printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", frad->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 { *(__be16*)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) { state(hdlc)->dce_changed = 1; state(hdlc)->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)) { state(hdlc)->dce_pvc_count--; state(hdlc)->dce_changed = 1; } delete_unused_pvcs(hdlc); return 0;}static void fr_destroy(struct net_device *frad){ hdlc_device *hdlc = dev_to_hdlc(frad); pvc_device *pvc = state(hdlc)->first_pvc; state(hdlc)->first_pvc = NULL; /* All PVCs destroyed */ state(hdlc)->dce_pvc_count = 0; state(hdlc)->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; }}static struct hdlc_proto proto = { .close = fr_close, .start = fr_start, .stop = fr_stop, .detach = fr_destroy, .ioctl = fr_ioctl, .module = THIS_MODULE,};static int 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: if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */ return -EINVAL; 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, &state(hdlc)->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 (dev_to_hdlc(dev)->proto != &proto) { /* Different proto */ result = attach_hdlc_protocol(dev, &proto, fr_rx, sizeof(struct frad_state)); if (result) return result; state(hdlc)->first_pvc = NULL; state(hdlc)->dce_pvc_count = 0; } memcpy(&state(hdlc)->settings, &new_settings, size); dev->hard_start_xmit = hdlc->xmit; dev->type = ARPHRD_FRAD; 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 (dev_to_hdlc(dev)->proto != &proto) /* Different proto */ return -EINVAL; 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;}static int __init mod_init(void){ register_hdlc_protocol(&proto); return 0;}static void __exit mod_exit(void){ unregister_hdlc_protocol(&proto);}module_init(mod_init);module_exit(mod_exit);MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");MODULE_DESCRIPTION("Frame-Relay protocol support for generic HDLC");MODULE_LICENSE("GPL v2");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -