?? hdlc.c
字號:
/* * Generic HDLC support routines for Linux * * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Current status: * - this is work in progress * - not heavily tested on SMP * - currently supported: * * raw IP-in-HDLC * * Cisco HDLC * * Frame Relay with ANSI or CCITT LMI (both user and network side) * * PPP (using syncppp.c) * * X.25 * * Use sethdlc utility to set line parameters, protocol and PVCs */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/sched.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/inetdevice.h>#include <linux/lapb.h>#include <linux/rtnetlink.h>#include <linux/hdlc.h>/* #define DEBUG_PKT *//* #define DEBUG_HARD_HEADER *//* #define DEBUG_FECN *//* #define DEBUG_BECN */static const char* version = "HDLC support module revision 1.02 for Linux 2.4";#define CISCO_MULTICAST 0x8F /* Cisco multicast address */#define CISCO_UNICAST 0x0F /* Cisco unicast address */#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */#define CISCO_ADDR_REQ 0 /* Cisco address request */#define CISCO_ADDR_REPLY 1 /* Cisco address reply */#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);/******************************************************** * * Cisco HDLC support * *******************************************************/static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, u16 type, void *daddr, void *saddr, unsigned int len){ hdlc_header *data;#ifdef DEBUG_HARD_HEADER printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);#endif skb_push(skb, sizeof(hdlc_header)); data = (hdlc_header*)skb->data; if (type == CISCO_KEEPALIVE) data->address = CISCO_MULTICAST; else data->address = CISCO_UNICAST; data->control = 0; data->protocol = htons(type); return sizeof(hdlc_header);}static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, u32 par1, u32 par2){ struct sk_buff *skb; cisco_packet *data; skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet)); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n", hdlc_to_name(hdlc)); return; } skb_reserve(skb, 4); cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE, NULL, NULL, 0); data = (cisco_packet*)skb->tail; data->type = htonl(type); data->par1 = htonl(par1); data->par2 = htonl(par2); data->rel = 0xFFFF; data->time = htonl(jiffies * 1000 / HZ); skb_put(skb, sizeof(cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); dev_queue_xmit(skb);}static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb){ hdlc_header *data = (hdlc_header*)skb->data; cisco_packet *cisco_data; struct in_device *in_dev; u32 addr, mask; if (skb->len<sizeof(hdlc_header)) goto rx_error; if (data->address != CISCO_MULTICAST && data->address != CISCO_UNICAST) goto rx_error; skb_pull(skb, sizeof(hdlc_header)); switch(ntohs(data->protocol)) { case ETH_P_IP: case ETH_P_IPX: case ETH_P_IPV6: skb->protocol = data->protocol; skb->dev = hdlc_to_dev(hdlc); netif_rx(skb); return; case CISCO_SYS_INFO: /* Packet is not needed, drop it. */ dev_kfree_skb_any(skb); return; case CISCO_KEEPALIVE: if (skb->len != CISCO_PACKET_LEN && skb->len != CISCO_BIG_PACKET_LEN) { printk(KERN_INFO "%s: Invalid length of Cisco " "control packet (%d bytes)\n", hdlc_to_name(hdlc), skb->len); goto rx_error; } cisco_data = (cisco_packet*)skb->data; switch(ntohl (cisco_data->type)) { case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ in_dev = hdlc_to_dev(hdlc)->ip_ptr; addr = 0; mask = ~0; /* is the mask correct? */ if (in_dev != NULL) { struct in_ifaddr **ifap = &in_dev->ifa_list; while (*ifap != NULL) { if (strcmp(hdlc_to_name(hdlc), (*ifap)->ifa_label) == 0) { addr = (*ifap)->ifa_local; mask = (*ifap)->ifa_mask; break; } ifap = &(*ifap)->ifa_next; } cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY, addr, mask); } dev_kfree_skb_any(skb); return; case CISCO_ADDR_REPLY: printk(KERN_INFO "%s: Unexpected Cisco IP address " "reply\n", hdlc_to_name(hdlc)); goto rx_error; case CISCO_KEEPALIVE_REQ: hdlc->lmi.rxseq = ntohl(cisco_data->par1); if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) { hdlc->lmi.last_poll = jiffies; if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) { u32 sec, min, hrs, days; sec = ntohl(cisco_data->time) / 1000; min = sec / 60; sec -= min * 60; hrs = min / 60; min -= hrs * 60; days = hrs / 24; hrs -= days * 24; printk(KERN_INFO "%s: Link up (peer " "uptime %ud%uh%um%us)\n", hdlc_to_name(hdlc), days, hrs, min, sec); } hdlc->lmi.state |= LINK_STATE_RELIABLE; } dev_kfree_skb_any(skb); return; } /* switch(keepalive type) */ } /* switch(protocol) */ printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc), data->protocol); dev_kfree_skb_any(skb); return; rx_error: hdlc->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb);}static void cisco_timer(unsigned long arg){ hdlc_device *hdlc = (hdlc_device*)arg; if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) { hdlc->lmi.state &= ~LINK_STATE_RELIABLE; printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc)); } cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq, hdlc->lmi.rxseq); hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; hdlc->timer.function = cisco_timer; hdlc->timer.data = arg; add_timer(&hdlc->timer);}/****************************************************************** * * generic Frame Relay routines * *****************************************************************/static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, u16 type, void *daddr, void *saddr, unsigned int len){ u16 head_len; if (!daddr) daddr = dev->broadcast;#ifdef DEBUG_HARD_HEADER printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);#endif switch(type) { case ETH_P_IP: head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IP; break; case ETH_P_IPV6: head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IPV6; break; case LMI_PROTO: head_len = 4; skb_push(skb, head_len); skb->data[3] = LMI_PROTO; 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; skb->data[8] = type>>8; skb->data[9] = (u8)type; } memcpy(skb->data, daddr, 2); skb->data[2] = FR_UI; return head_len;}static inline void fr_log_dlci_active(pvc_device *pvc){ printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), pvc->state & PVC_STATE_ACTIVE ? "" : "in", pvc->state & PVC_STATE_NEW ? " new" : "");}static inline u8 fr_lmi_nextseq(u8 x){ x++; return x ? x : 1;}static void fr_lmi_send(hdlc_device *hdlc, int fullrep){ struct sk_buff *skb; pvc_device *pvc = hdlc->first_pvc; int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; int stat_len = 3; u8 *data; int i = 0; if (mode_is(hdlc, MODE_DCE) && fullrep) { len += hdlc->pvc_count * (2 + stat_len); if (len > HDLC_MAX_MTU) { printk(KERN_WARNING "%s: Too many PVCs while sending " "LMI full report\n", hdlc_to_name(hdlc)); return; } } skb = dev_alloc_skb(len); if (!skb) { printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", hdlc_to_name(hdlc)); return; } memset(skb->data, 0, len); skb_reserve(skb, 4); fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); data = skb->tail; data[i++] = LMI_CALLREF; data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; if (mode_is(hdlc, MODE_FR_ANSI)) data[i++] = LMI_ANSI_LOCKSHIFT; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE; data[i++] = LMI_REPT_LEN; data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; data[i++] = LMI_INTEG_LEN; data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); data[i++] = hdlc->lmi.rxseq; if (mode_is(hdlc, MODE_DCE) && fullrep) { while (pvc) { data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_PVCSTAT:LMI_PVCSTAT; data[i++] = stat_len; if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && (pvc->netdev.flags & IFF_UP) && !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { pvc->state |= PVC_STATE_NEW; fr_log_dlci_active(pvc); } dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), data+i, pvc->state); i += stat_len; pvc = pvc->next; } } skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); dev_queue_xmit(skb);}static void fr_timer(unsigned long arg){ hdlc_device *hdlc = (hdlc_device*)arg; int i, cnt = 0, reliable; u32 list; if (mode_is(hdlc, MODE_DCE)) reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); else { hdlc->lmi.last_errors <<= 1; /* Shift the list */ if (hdlc->lmi.state & LINK_STATE_REQUEST) { printk(KERN_INFO "%s: No LMI status reply received\n", hdlc_to_name(hdlc)); hdlc->lmi.last_errors |= 1; } for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393; i++, list >>= 1) cnt += (list & 1); /* errors count */ reliable = (cnt < hdlc->lmi.N392); } if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != (reliable ? LINK_STATE_RELIABLE : 0)) { pvc_device *pvc = hdlc->first_pvc; while (pvc) {/* Deactivate all PVCs */ pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); pvc = pvc->next; } hdlc->lmi.state ^= LINK_STATE_RELIABLE; printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc), reliable ? "" : "un"); if (reliable) { hdlc->lmi.N391cnt = 0; /* Request full status */ hdlc->lmi.state |= LINK_STATE_CHANGED; } } if (mode_is(hdlc, MODE_DCE)) hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; else { if (hdlc->lmi.N391cnt) hdlc->lmi.N391cnt--; fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); hdlc->lmi.state |= LINK_STATE_REQUEST; hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; } hdlc->timer.function = fr_timer; hdlc->timer.data = arg; add_timer(&hdlc->timer);}static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb){ int stat_len; pvc_device *pvc; int reptype = -1, error; u8 rxseq, txseq; int i; if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH)) { printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc)); return 1; } if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY)) { printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", hdlc_to_name(hdlc), skb->data[2], mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); return 1; } i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { printk(KERN_INFO "%s: Not a report type=%x\n", hdlc_to_name(hdlc), skb->data[i]); return 1; } i++; i++; /* Skip length field */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -