📄 hdlc.c
字号:
reptype = skb->data[i++]; if (skb->data[i]!= (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { printk(KERN_INFO "%s: Unsupported status element=%x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; i++; /* Skip length field */ hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ rxseq = skb->data[i++]; /* Should confirm our sequence */ txseq = hdlc->lmi.txseq; if (mode_is(hdlc, MODE_DCE)) { if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { printk(KERN_INFO "%s: Unsupported report type=%x\n", hdlc_to_name(hdlc), reptype); return 1; } } error = 0; if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) error = 1; if (rxseq == 0 || rxseq != txseq) { hdlc->lmi.N391cnt = 0; /* Ask for full report next time */ error = 1; } if (mode_is(hdlc, MODE_DCE)) { if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) {/* Stop sending full report - the last one has been confirmed by DTE */ hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; pvc = hdlc->first_pvc; while (pvc) { if (pvc->state & PVC_STATE_NEW) { pvc->state &= ~PVC_STATE_NEW; pvc->state |= PVC_STATE_ACTIVE; fr_log_dlci_active(pvc);/* Tell DTE that new PVC is now active */ hdlc->lmi.state |= LINK_STATE_CHANGED; } pvc = pvc->next; } } if (hdlc->lmi.state & LINK_STATE_CHANGED) { reptype = LMI_FULLREP; hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; hdlc->lmi.state &= ~LINK_STATE_CHANGED; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); return 0; } /* DTE */ if (reptype != LMI_FULLREP || error) return 0; stat_len = 3; pvc = hdlc->first_pvc; while (pvc) { pvc->newstate = 0; pvc = pvc->next; } while (skb->len >= i + 2 + stat_len) { u16 dlci; u8 state = 0; if (skb->data[i] != (mode_is(hdlc, MODE_FR_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, &state); pvc = find_pvc(hdlc, dlci); if (pvc) pvc->newstate = state; else if (state == PVC_STATE_NEW) printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", hdlc_to_name(hdlc), dlci); i += stat_len; } pvc = hdlc->first_pvc; while (pvc) { if (pvc->newstate == PVC_STATE_NEW) pvc->newstate = PVC_STATE_ACTIVE; pvc->newstate |= (pvc->state & ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); if (pvc->state != pvc->newstate) { pvc->state = pvc->newstate; fr_log_dlci_active(pvc); } pvc = pvc->next; } /* Next full report after N391 polls */ hdlc->lmi.N391cnt = hdlc->lmi.N391; return 0;}static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb){ 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 (data[3] == LMI_PROTO) { if (fr_lmi_recv(hdlc, skb)) goto rx_error; else { /* No request pending */ hdlc->lmi.state &= ~LINK_STATE_REQUEST; hdlc->lmi.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 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 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 & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) {#ifdef DEBUG_FECN printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), fh->fecn ? "N" : "FF");#endif pvc->state ^= PVC_STATE_FECN; } if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) {#ifdef DEBUG_FECN printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), fh->becn ? "N" : "FF");#endif pvc->state ^= PVC_STATE_BECN; } if (pvc->state & PVC_STATE_BECN) pvc->stats.rx_compressed++; if (data[3] == NLPID_IP) { skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb->protocol = htons(ETH_P_IP); skb->dev = &pvc->netdev; 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); skb->dev = &pvc->netdev; netif_rx(skb); return; } if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD && data[6] == FR_PAD && data[7] == FR_PAD && ((data[8]<<8) | data[9]) == ETH_P_ARP) { skb_pull(skb, 10); skb->protocol = htons(ETH_P_ARP); skb->dev = &pvc->netdev; netif_rx(skb); return; } printk(KERN_INFO "%s: Unusupported protocol %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 void fr_cisco_open(hdlc_device *hdlc){ hdlc->lmi.state = LINK_STATE_CHANGED; hdlc->lmi.txseq = hdlc->lmi.rxseq = 0; hdlc->lmi.last_errors = 0xFFFFFFFF; hdlc->lmi.N391cnt = 0; init_timer(&hdlc->timer); hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */ hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer; hdlc->timer.data = (unsigned long)hdlc; add_timer(&hdlc->timer);}static void fr_cisco_close(hdlc_device *hdlc){ pvc_device *pvc = hdlc->first_pvc; del_timer_sync(&hdlc->timer); while(pvc) { /* NULL in Cisco mode */ dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */ pvc = pvc->next; }}/****************************************************************** * * generic HDLC routines * *****************************************************************/static int hdlc_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;}/******************************************************** * * PVC device routines * *******************************************************/static int pvc_open(struct net_device *dev){ pvc_device *pvc = dev_to_pvc(dev); int result = 0; if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) return -EIO; /* Master must be UP in order to activate PVC */ memset(&(pvc->stats), 0, sizeof(struct net_device_stats)); pvc->state = 0; if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc) result = pvc->master->open_pvc(pvc); if (result) return result; pvc->master->lmi.state |= LINK_STATE_CHANGED; return 0;}static int pvc_close(struct net_device *dev){ pvc_device *pvc = dev_to_pvc(dev); pvc->state = 0; if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc) pvc->master->close_pvc(pvc); pvc->master->lmi.state |= LINK_STATE_CHANGED; return 0;}static int pvc_xmit(struct sk_buff *skb, struct net_device *dev){ pvc_device *pvc = dev_to_pvc(dev); if (pvc->state & PVC_STATE_ACTIVE) { skb->dev = hdlc_to_dev(pvc->master); pvc->stats.tx_bytes += skb->len; pvc->stats.tx_packets++; if (pvc->state & PVC_STATE_FECN) pvc->stats.tx_compressed++; /* TX Congestion counter */ dev_queue_xmit(skb); } else { pvc->stats.tx_dropped++; dev_kfree_skb(skb); } return 0;}static struct net_device_stats *pvc_get_stats(struct net_device *dev){ pvc_device *pvc = dev_to_pvc(dev); return &pvc->stats;}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 void destroy_pvc_list(hdlc_device *hdlc){ pvc_device *pvc = hdlc->first_pvc; while(pvc) { pvc_device *next = pvc->next; unregister_netdevice(&pvc->netdev); kfree(pvc); pvc = next; } hdlc->first_pvc = NULL; /* All PVCs destroyed */ hdlc->pvc_count = 0; hdlc->lmi.state |= LINK_STATE_CHANGED;}/******************************************************** * * X.25 protocol support routines * *******************************************************/#ifdef CONFIG_HDLC_X25/* These functions are callbacks called by LAPB layer */void x25_connect_disconnect(void *token, int reason, int code){ hdlc_device *hdlc = token; struct sk_buff *skb; unsigned char *ptr; if ((skb = dev_alloc_skb(1)) == NULL) { printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc)); return; } ptr = skb_put(skb, 1); *ptr = code; skb->dev = hdlc_to_dev(hdlc); skb->protocol = htons(ETH_P_X25); skb->mac.raw = skb->data; skb->pkt_type = PACKET_HOST; netif_rx(skb);}void x25_connected(void *token, int reason){ x25_connect_disconnect(token, reason, 1);}void x25_disconnected(void *token, int reason){ x25_connect_disconnect(token, reason, 2);}int x25_data_indication(void *token, struct sk_buff *skb){ hdlc_device *hdlc = token; unsigned char *ptr; ptr = skb_push(skb, 1); *ptr = 0; skb->dev = hdlc_to_dev(hdlc); skb->protocol = htons(ETH_P_X25); skb->mac.raw = skb->data; skb->pkt_type = PACKET_HOST; return netif_rx(skb);}void x25_data_transmit(void *token, struct sk_buff *skb){ hdlc_device *hdlc = token; hdlc->xmit(hdlc, skb); /* Ignore return value :-( */}#endif /* CONFIG_HDLC_X25 *//******************************************************** * * HDLC device routines * *******************************************************/static int hdlc_open(struct net_device *dev){ hdlc_device *hdlc = dev_to_hdlc(dev); int result; if (hdlc->mode == MODE_NONE) return -ENOSYS; memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); if (mode_is(hdlc, MODE_FR | MODE_SOFT) || mode_is(hdlc, MODE_CISCO | MODE_SOFT)) fr_cisco_open(hdlc);#ifdef CONFIG_HDLC_PPP else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { sppp_attach(&hdlc->pppdev); /* sppp_attach nukes them. We don't need syncppp's ioctl */ dev->do_ioctl = hdlc_ioctl; hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO; dev->type = ARPHRD_PPP; result = sppp_open(dev); if (result) { sppp_detach(dev); return result; } }#endif#ifdef CONFIG_HDLC_X25
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -