📄 hdlc_fr.c
字号:
/* * 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 + -