?? hdlc_fr.c
字號(hào):
if (hdlc->state.fr.changed) { reptype = LMI_FULLREP; hdlc->state.fr.fullrep_sent = 1; hdlc->state.fr.changed = 0; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); return 0; } /* DTE */ if (reptype != LMI_FULLREP || error) return 0; stat_len = 3; pvc = hdlc->state.fr.first_pvc; while (pvc) { pvc->state.deleted = pvc->state.active; /* mark active PVCs */ pvc = pvc->next; } while (skb->len >= i + 2 + stat_len) { u16 dlci; int active, new; if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; if (skb->data[i] != stat_len) { printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); pvc = find_pvc(hdlc, dlci); active |= new; if (pvc) { if (active && !pvc->state.active && (pvc->netdev.flags & IFF_UP)) { pvc->state.active = active; fr_log_dlci_active(pvc); } pvc->state.deleted = 0; } else if (new) printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", hdlc_to_name(hdlc), dlci); i += stat_len; } pvc = hdlc->state.fr.first_pvc; while (pvc) { if (pvc->state.deleted) { pvc->state.active = pvc->state.new = 0; fr_log_dlci_active(pvc); pvc->state.deleted = 0; } pvc = pvc->next; } /* Next full report after N391 polls */ hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391; return 0;}static void fr_rx(struct sk_buff *skb){ hdlc_device *hdlc = dev_to_hdlc(skb->dev); fr_hdr *fh = (fr_hdr*)skb->data; u8 *data = skb->data; u16 dlci; pvc_device *pvc; if (skb->len<4 || fh->ea1 || data[2] != FR_UI) goto rx_error; dlci = q922_to_dlci(skb->data); if (dlci == LMI_DLCI) { if (hdlc->state.fr.settings.lmi == LMI_NONE) goto rx_error; /* LMI packet with no LMI? */ if (data[3] == LMI_PROTO) { if (fr_lmi_recv(hdlc, skb)) goto rx_error; else { /* No request pending */ hdlc->state.fr.request = 0; hdlc->state.fr.last_poll = jiffies; dev_kfree_skb_any(skb); return; } } printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", hdlc_to_name(hdlc)); goto rx_error; } pvc = find_pvc(hdlc, dlci); if (!pvc) {#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n", hdlc_to_name(hdlc), dlci);#endif goto rx_error; } if ((pvc->netdev.flags & IFF_UP) == 0) {#ifdef CONFIG_HDLC_DEBUG_PKT printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n", hdlc_to_name(hdlc), dlci);#endif goto rx_error; } pvc->stats.rx_packets++; /* PVC traffic */ pvc->stats.rx_bytes += skb->len; if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {#ifdef CONFIG_HDLC_DEBUG_ECN printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), fh->fecn ? "N" : "FF");#endif pvc->state.fecn ^= 1; } if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {#ifdef CONFIG_HDLC_DEBUG_ECN printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), fh->becn ? "N" : "FF");#endif pvc->state.becn ^= 1; } if (pvc->state.becn) pvc->stats.rx_compressed++; skb->dev = &pvc->netdev; if (data[3] == NLPID_IP) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb->protocol = htons(ETH_P_IP); netif_rx(skb); return; } if (data[3] == NLPID_IPV6) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb->protocol = htons(ETH_P_IPV6); netif_rx(skb); return; } if (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: skb->protocol = htons(pid); break; default: printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " "PID=%x\n", hdlc_to_name(hdlc), oui, pid); dev_kfree_skb_any(skb); return; } netif_rx(skb); return; } printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", hdlc_to_name(hdlc), data[3]); dev_kfree_skb_any(skb); return; rx_error: hdlc->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb);}static int fr_open(hdlc_device *hdlc){ if (hdlc->state.fr.settings.lmi != LMI_NONE) { hdlc->state.fr.last_poll = 0; hdlc->state.fr.reliable = 0; hdlc->state.fr.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)hdlc; add_timer(&hdlc->state.fr.timer); } else hdlc->state.fr.reliable = 1; return 0;}static void fr_close(hdlc_device *hdlc){ pvc_device *pvc = hdlc->state.fr.first_pvc; if (hdlc->state.fr.settings.lmi != LMI_NONE) del_timer_sync(&hdlc->state.fr.timer); while(pvc) { dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ pvc = pvc->next; }}static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create){ pvc_device **pvc_p = &hdlc->state.fr.first_pvc; pvc_device *pvc; int result; if(dlci <= 0 || dlci >= 1024) return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ while(*pvc_p) { if (netdev_dlci(&(*pvc_p)->netdev) == dlci) break; pvc_p = &(*pvc_p)->next; } if (create) { /* Create PVC */ if (*pvc_p != NULL) return -EEXIST; pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); if (!pvc) { printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n", hdlc_to_name(hdlc)); return -ENOBUFS; } memset(pvc, 0, sizeof(pvc_device)); pvc->netdev.hard_start_xmit = pvc_xmit; pvc->netdev.get_stats = pvc_get_stats; pvc->netdev.open = pvc_open; pvc->netdev.stop = pvc_close; pvc->netdev.change_mtu = pvc_change_mtu; pvc->netdev.mtu = HDLC_MAX_MTU; pvc->netdev.type = ARPHRD_DLCI; pvc->netdev.hard_header_len = 16; pvc->netdev.hard_header = fr_hard_header; pvc->netdev.tx_queue_len = 0; pvc->netdev.flags = IFF_POINTOPOINT; pvc->master = hdlc; *(u16*)pvc->netdev.dev_addr = htons(dlci); dlci_to_q922(pvc->netdev.broadcast, dlci); pvc->netdev.addr_len = 2; result = dev_alloc_name(&pvc->netdev, "pvc%d"); if (result < 0) { kfree(pvc); *pvc_p = NULL; return result; } if (register_netdevice(&pvc->netdev) != 0) { kfree(pvc); *pvc_p = NULL; return -EIO; } hdlc->state.fr.changed = 1; hdlc->state.fr.pvc_count++; return 0; } if (*pvc_p == NULL) /* Delete PVC */ return -ENOENT; pvc = *pvc_p; if (pvc->netdev.flags & IFF_UP) return -EBUSY; /* PVC in use */ hdlc->state.fr.changed = 1; hdlc->state.fr.pvc_count--; *pvc_p = pvc->next; unregister_netdevice(&pvc->netdev); kfree(pvc); return 0;}static void fr_destroy(hdlc_device *hdlc){ pvc_device *pvc = hdlc->state.fr.first_pvc; while(pvc) { pvc_device *next = pvc->next; unregister_netdev(&pvc->netdev); kfree(pvc); pvc = next; } hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ hdlc->state.fr.pvc_count = 0; hdlc->state.fr.changed = 1;}int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr){ fr_proto *fr_s = ifr->ifr_settings.ifs_ifsu.fr; const size_t size = sizeof(fr_proto); fr_proto new_settings; struct net_device *dev = hdlc_to_dev(hdlc); 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.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(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); if (result) return result; if (hdlc->proto != IF_PROTO_FR) { hdlc_proto_detach(hdlc); hdlc->state.fr.first_pvc = NULL; hdlc->state.fr.pvc_count = 0; } memcpy(&hdlc->state.fr.settings, &new_settings, size); hdlc->open = fr_open; hdlc->stop = fr_close; hdlc->netif_rx = fr_rx; hdlc->proto_detach = fr_destroy; hdlc->proto = IF_PROTO_FR; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = fr_hard_header; dev->type = ARPHRD_FRAD; dev->addr_len = 2; *(u16*)dev->dev_addr = htons(LMI_DLCI); dlci_to_q922(dev->broadcast, LMI_DLCI); return 0; case IF_PROTO_FR_ADD_PVC: case IF_PROTO_FR_DEL_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; return fr_pvc(hdlc, pvc.dlci, ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC); } return -EINVAL;}
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -