[PATCH] Generic HDLC update

The attached patch updates generic HDLC to version 1.18.
FR Cisco LMI production-tested. Please apply to Linux 2.6. Thanks.

Changes:
- doc updates
- added Cisco LMI support to Frame-Relay code
- cleaned hdlc_fr.c a bit, removed some orphaned #defines etc.
- fixed a problem with non-functional LMI in FR DCE mode.
- changed diagnostic messages to better conform to FR standards
- all protocols: information about carrier changes (DCD line) is now
  printed to kernel logs.

Signed-Off-By: Krzysztof Halasa <khc@pm.waw.pl>
This commit is contained in:
Krzysztof Halasa 2005-04-21 15:57:25 +02:00 committed by Jeff Garzik
parent 88d7bd8cb9
commit b3dd65f958
4 changed files with 211 additions and 170 deletions

View File

@ -1,21 +1,21 @@
Generic HDLC layer Generic HDLC layer
Krzysztof Halasa <khc@pm.waw.pl> Krzysztof Halasa <khc@pm.waw.pl>
January, 2003
Generic HDLC layer currently supports: Generic HDLC layer currently supports:
- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP). 1. Frame Relay (ANSI, CCITT, Cisco and no LMI).
Normal (routed) and Ethernet-bridged (Ethernet device emulation) - Normal (routed) and Ethernet-bridged (Ethernet device emulation)
interfaces can share a single PVC. interfaces can share a single PVC.
- raw HDLC - either IP (IPv4) interface or Ethernet device emulation. - ARP support (no InARP support in the kernel - there is an
- Cisco HDLC, experimental InARP user-space daemon available on:
- PPP (uses syncppp.c), http://www.kernel.org/pub/linux/utils/net/hdlc/).
- X.25 (uses X.25 routines). 2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
3. Cisco HDLC.
4. PPP (uses syncppp.c).
5. X.25 (uses X.25 routines).
There are hardware drivers for the following cards: Generic HDLC is a protocol driver only - it needs a low-level driver
- C101 by Moxa Technologies Co., Ltd. for your particular hardware.
- RISCom/N2 by SDL Communications Inc.
- and others, some not in the official kernel.
Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging). with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
@ -24,7 +24,7 @@ with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
Make sure the hdlc.o and the hardware driver are loaded. It should Make sure the hdlc.o and the hardware driver are loaded. It should
create a number of "hdlc" (hdlc0 etc) network devices, one for each create a number of "hdlc" (hdlc0 etc) network devices, one for each
WAN port. You'll need the "sethdlc" utility, get it from: WAN port. You'll need the "sethdlc" utility, get it from:
http://hq.pm.waw.pl/hdlc/ http://www.kernel.org/pub/linux/utils/net/hdlc/
Compile sethdlc.c utility: Compile sethdlc.c utility:
gcc -O2 -Wall -o sethdlc sethdlc.c gcc -O2 -Wall -o sethdlc sethdlc.c
@ -52,12 +52,12 @@ Setting interface:
* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port * v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
if the card has software-selectable interfaces if the card has software-selectable interfaces
loopback - activate hardware loopback (for testing only) loopback - activate hardware loopback (for testing only)
* clock ext - external clock (uses DTE RX and TX clock) * clock ext - both RX clock and TX clock external
* clock int - internal clock (provides clock signal on DCE clock output) * clock int - both RX clock and TX clock internal
* clock txint - TX internal, RX external (provides TX clock on DCE output) * clock txint - RX clock external, TX clock internal
* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output) * clock txfromrx - RX clock external, TX clock derived from RX clock
* rate - sets clock rate in bps (not required for external clock or * rate - sets clock rate in bps (for "int" or "txint" clock only)
for txfromrx)
Setting protocol: Setting protocol:
@ -79,7 +79,7 @@ Setting protocol:
* x25 - sets X.25 mode * x25 - sets X.25 mode
* fr - Frame Relay mode * fr - Frame Relay mode
lmi ansi / ccitt / none - LMI (link management) type lmi ansi / ccitt / cisco / none - LMI (link management) type
dce - Frame Relay DCE (network) side LMI instead of default DTE (user). dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
It has nothing to do with clocks! It has nothing to do with clocks!
t391 - link integrity verification polling timer (in seconds) - user t391 - link integrity verification polling timer (in seconds) - user
@ -119,13 +119,14 @@ or
If you have a problem with N2 or C101 card, you can issue the "private" If you have a problem with N2, C101 or PLX200SYN card, you can issue the
command to see port's packet descriptor rings (in kernel logs): "private" command to see port's packet descriptor rings (in kernel logs):
sethdlc hdlc0 private sethdlc hdlc0 private
The hardware driver has to be build with CONFIG_HDLC_DEBUG_RINGS. The hardware driver has to be build with #define DEBUG_RINGS.
Attaching this info to bug reports would be helpful. Anyway, let me know Attaching this info to bug reports would be helpful. Anyway, let me know
if you have problems using this. if you have problems using this.
For patches and other info look at http://hq.pm.waw.pl/hdlc/ For patches and other info look at:
<http://www.kernel.org/pub/linux/utils/net/hdlc/>.

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* Frame Relay support * Frame Relay support
* *
* Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl> * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of version 2 of the GNU General Public License
@ -27,6 +27,10 @@
active = open and "link reliable" active = open and "link reliable"
exist = new = not used 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/module.h>
@ -49,45 +53,41 @@
#undef DEBUG_ECN #undef DEBUG_ECN
#undef DEBUG_LINK #undef DEBUG_LINK
#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ #define FR_UI 0x03
#define FR_PAD 0x00
#define PVC_STATE_NEW 0x01 #define NLPID_IP 0xCC
#define PVC_STATE_ACTIVE 0x02 #define NLPID_IPV6 0x8E
#define PVC_STATE_FECN 0x08 /* FECN condition */ #define NLPID_SNAP 0x80
#define PVC_STATE_BECN 0x10 /* BECN condition */ #define NLPID_PAD 0x00
#define NLPID_CCITT_ANSI_LMI 0x08
#define NLPID_CISCO_LMI 0x09
#define FR_UI 0x03 #define LMI_CCITT_ANSI_DLCI 0 /* LMI DLCI */
#define FR_PAD 0x00 #define LMI_CISCO_DLCI 1023
#define NLPID_IP 0xCC #define LMI_CALLREF 0x00 /* Call Reference */
#define NLPID_IPV6 0x8E #define LMI_ANSI_LOCKSHIFT 0x95 /* ANSI locking shift */
#define NLPID_SNAP 0x80 #define LMI_ANSI_CISCO_REPTYPE 0x01 /* report type */
#define NLPID_PAD 0x00 #define LMI_CCITT_REPTYPE 0x51
#define NLPID_Q933 0x08 #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_DLCI 0 /* LMI DLCI */
#define LMI_PROTO 0x08
#define LMI_CALLREF 0x00 /* Call Reference */
#define LMI_ANSI_LOCKSHIFT 0x95 /* ANSI lockshift */
#define LMI_REPTYPE 1 /* report type */
#define LMI_CCITT_REPTYPE 0x51
#define LMI_ALIVE 3 /* keep alive */
#define LMI_CCITT_ALIVE 0x53
#define LMI_PVCSTAT 7 /* pvc status */
#define LMI_CCITT_PVCSTAT 0x57
#define LMI_FULLREP 0 /* full report */
#define LMI_INTEGRITY 1 /* link integrity report */
#define LMI_SINGLE 2 /* single pvc report */
#define LMI_STATUS_ENQUIRY 0x75 #define LMI_STATUS_ENQUIRY 0x75
#define LMI_STATUS 0x7D /* reply */ #define LMI_STATUS 0x7D /* reply */
#define LMI_REPT_LEN 1 /* report type element length */ #define LMI_REPT_LEN 1 /* report type element length */
#define LMI_INTEG_LEN 2 /* link integrity element length */ #define LMI_INTEG_LEN 2 /* link integrity element length */
#define LMI_LENGTH 13 /* standard LMI frame length */ #define LMI_CCITT_CISCO_LENGTH 13 /* LMI frame lengths */
#define LMI_ANSI_LENGTH 14 #define LMI_ANSI_LENGTH 14
typedef struct { typedef struct {
@ -223,35 +223,24 @@ static inline struct net_device** get_dev_p(pvc_device *pvc, int type)
} }
static inline u16 status_to_dlci(u8 *status, int *active, int *new)
{
*new = (status[2] & 0x08) ? 1 : 0;
*active = (status[2] & 0x02) ? 1 : 0;
return ((status[0] & 0x3F) << 4) | ((status[1] & 0x78) >> 3);
}
static inline void dlci_to_status(u16 dlci, u8 *status, int active, int new)
{
status[0] = (dlci >> 4) & 0x3F;
status[1] = ((dlci << 3) & 0x78) | 0x80;
status[2] = 0x80;
if (new)
status[2] |= 0x08;
else if (active)
status[2] |= 0x02;
}
static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
{ {
u16 head_len; u16 head_len;
struct sk_buff *skb = *skb_p; struct sk_buff *skb = *skb_p;
switch (skb->protocol) { switch (skb->protocol) {
case __constant_ntohs(NLPID_CCITT_ANSI_LMI):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = NLPID_CCITT_ANSI_LMI;
break;
case __constant_ntohs(NLPID_CISCO_LMI):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = NLPID_CISCO_LMI;
break;
case __constant_ntohs(ETH_P_IP): case __constant_ntohs(ETH_P_IP):
head_len = 4; head_len = 4;
skb_push(skb, head_len); skb_push(skb, head_len);
@ -264,12 +253,6 @@ static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
skb->data[3] = NLPID_IPV6; skb->data[3] = NLPID_IPV6;
break; break;
case __constant_ntohs(LMI_PROTO):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = LMI_PROTO;
break;
case __constant_ntohs(ETH_P_802_3): case __constant_ntohs(ETH_P_802_3):
head_len = 10; head_len = 10;
if (skb_headroom(skb) < head_len) { if (skb_headroom(skb) < head_len) {
@ -461,13 +444,14 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
hdlc_device *hdlc = dev_to_hdlc(dev); hdlc_device *hdlc = dev_to_hdlc(dev);
struct sk_buff *skb; struct sk_buff *skb;
pvc_device *pvc = hdlc->state.fr.first_pvc; pvc_device *pvc = hdlc->state.fr.first_pvc;
int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH int lmi = hdlc->state.fr.settings.lmi;
: LMI_LENGTH; int dce = hdlc->state.fr.settings.dce;
int stat_len = 3; int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3;
u8 *data; u8 *data;
int i = 0; int i = 0;
if (hdlc->state.fr.settings.dce && fullrep) { if (dce && fullrep) {
len += hdlc->state.fr.dce_pvc_count * (2 + stat_len); len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MRU) { if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending " printk(KERN_WARNING "%s: Too many PVCs while sending "
@ -484,29 +468,31 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
} }
memset(skb->data, 0, len); memset(skb->data, 0, len);
skb_reserve(skb, 4); skb_reserve(skb, 4);
skb->protocol = __constant_htons(LMI_PROTO); if (lmi == LMI_CISCO) {
fr_hard_header(&skb, LMI_DLCI); skb->protocol = __constant_htons(NLPID_CISCO_LMI);
fr_hard_header(&skb, LMI_CISCO_DLCI);
} else {
skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI);
fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
}
data = skb->tail; data = skb->tail;
data[i++] = LMI_CALLREF; data[i++] = LMI_CALLREF;
data[i++] = hdlc->state.fr.settings.dce data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY;
? LMI_STATUS : LMI_STATUS_ENQUIRY; if (lmi == LMI_ANSI)
if (hdlc->state.fr.settings.lmi == LMI_ANSI)
data[i++] = LMI_ANSI_LOCKSHIFT; data[i++] = LMI_ANSI_LOCKSHIFT;
data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) data[i++] = lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :
? LMI_CCITT_REPTYPE : LMI_REPTYPE; LMI_ANSI_CISCO_REPTYPE;
data[i++] = LMI_REPT_LEN; data[i++] = LMI_REPT_LEN;
data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE;
data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
? LMI_CCITT_ALIVE : LMI_ALIVE;
data[i++] = LMI_INTEG_LEN; data[i++] = LMI_INTEG_LEN;
data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
data[i++] = hdlc->state.fr.rxseq; data[i++] = hdlc->state.fr.rxseq;
if (hdlc->state.fr.settings.dce && fullrep) { if (dce && fullrep) {
while (pvc) { while (pvc) {
data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) data[i++] = lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :
? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; LMI_ANSI_CISCO_PVCSTAT;
data[i++] = stat_len; data[i++] = stat_len;
/* LMI start/restart */ /* LMI start/restart */
@ -523,8 +509,20 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
fr_log_dlci_active(pvc); fr_log_dlci_active(pvc);
} }
dlci_to_status(pvc->dlci, data + i, if (lmi == LMI_CISCO) {
pvc->state.active, pvc->state.new); data[i] = pvc->dlci >> 8;
data[i + 1] = pvc->dlci & 0xFF;
} else {
data[i] = (pvc->dlci >> 4) & 0x3F;
data[i + 1] = ((pvc->dlci << 3) & 0x78) | 0x80;
data[i + 2] = 0x80;
}
if (pvc->state.new)
data[i + 2] |= 0x08;
else if (pvc->state.active)
data[i + 2] |= 0x02;
i += stat_len; i += stat_len;
pvc = pvc->next; pvc = pvc->next;
} }
@ -569,6 +567,8 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
pvc_carrier(0, pvc); pvc_carrier(0, pvc);
pvc->state.exist = pvc->state.active = 0; pvc->state.exist = pvc->state.active = 0;
pvc->state.new = 0; pvc->state.new = 0;
if (!hdlc->state.fr.settings.dce)
pvc->state.bandwidth = 0;
pvc = pvc->next; pvc = pvc->next;
} }
} }
@ -583,11 +583,12 @@ static void fr_timer(unsigned long arg)
int i, cnt = 0, reliable; int i, cnt = 0, reliable;
u32 list; u32 list;
if (hdlc->state.fr.settings.dce) if (hdlc->state.fr.settings.dce) {
reliable = hdlc->state.fr.request && reliable = hdlc->state.fr.request &&
time_before(jiffies, hdlc->state.fr.last_poll + time_before(jiffies, hdlc->state.fr.last_poll +
hdlc->state.fr.settings.t392 * HZ); hdlc->state.fr.settings.t392 * HZ);
else { hdlc->state.fr.request = 0;
} else {
hdlc->state.fr.last_errors <<= 1; /* Shift the list */ hdlc->state.fr.last_errors <<= 1; /* Shift the list */
if (hdlc->state.fr.request) { if (hdlc->state.fr.request) {
if (hdlc->state.fr.reliable) if (hdlc->state.fr.reliable)
@ -634,65 +635,88 @@ static void fr_timer(unsigned long arg)
static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
{ {
hdlc_device *hdlc = dev_to_hdlc(dev); hdlc_device *hdlc = dev_to_hdlc(dev);
int stat_len;
pvc_device *pvc; pvc_device *pvc;
int reptype = -1, error, no_ram;
u8 rxseq, txseq; u8 rxseq, txseq;
int i; int lmi = hdlc->state.fr.settings.lmi;
int dce = hdlc->state.fr.settings.dce;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;
if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI) if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
? LMI_ANSI_LENGTH : LMI_LENGTH)) { LMI_CCITT_CISCO_LENGTH)) {
printk(KERN_INFO "%s: Short LMI frame\n", dev->name); printk(KERN_INFO "%s: Short LMI frame\n", dev->name);
return 1; return 1;
} }
if (skb->data[5] != (!hdlc->state.fr.settings.dce ? if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
LMI_STATUS : LMI_STATUS_ENQUIRY)) { NLPID_CCITT_ANSI_LMI)) {
printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", printk(KERN_INFO "%s: Received non-LMI frame with LMI"
dev->name, skb->data[2], " DLCI\n", dev->name);
hdlc->state.fr.settings.dce ? "enquiry" : "reply");
return 1; return 1;
} }
i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6; if (skb->data[4] != LMI_CALLREF) {
printk(KERN_INFO "%s: Invalid LMI Call reference (0x%02X)\n",
dev->name, skb->data[4]);
return 1;
}
if (skb->data[i] != if (skb->data[5] != (dce ? LMI_STATUS_ENQUIRY : LMI_STATUS)) {
((hdlc->state.fr.settings.lmi == LMI_CCITT) printk(KERN_INFO "%s: Invalid LMI Message type (0x%02X)\n",
? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { dev->name, skb->data[5]);
printk(KERN_INFO "%s: Not a report type=%x\n", return 1;
}
if (lmi == LMI_ANSI) {
if (skb->data[6] != LMI_ANSI_LOCKSHIFT) {
printk(KERN_INFO "%s: Not ANSI locking shift in LMI"
" message (0x%02X)\n", dev->name, skb->data[6]);
return 1;
}
i = 7;
} else
i = 6;
if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :
LMI_ANSI_CISCO_REPTYPE)) {
printk(KERN_INFO "%s: Not an LMI Report type IE (0x%02X)\n",
dev->name, skb->data[i]); dev->name, skb->data[i]);
return 1; return 1;
} }
i++;
i++; /* Skip length field */ if (skb->data[++i] != LMI_REPT_LEN) {
printk(KERN_INFO "%s: Invalid LMI Report type IE length"
" (%u)\n", dev->name, skb->data[i]);
return 1;
}
reptype = skb->data[i++]; reptype = skb->data[++i];
if (reptype != LMI_INTEGRITY && reptype != LMI_FULLREP) {
printk(KERN_INFO "%s: Unsupported LMI Report type (0x%02X)\n",
dev->name, reptype);
return 1;
}
if (skb->data[i]!= if (skb->data[++i] != (lmi == LMI_CCITT ? LMI_CCITT_ALIVE :
((hdlc->state.fr.settings.lmi == LMI_CCITT) LMI_ANSI_CISCO_ALIVE)) {
? LMI_CCITT_ALIVE : LMI_ALIVE)) { printk(KERN_INFO "%s: Not an LMI Link integrity verification"
printk(KERN_INFO "%s: Unsupported status element=%x\n", " IE (0x%02X)\n", dev->name, skb->data[i]);
dev->name, skb->data[i]); return 1;
}
if (skb->data[++i] != LMI_INTEG_LEN) {
printk(KERN_INFO "%s: Invalid LMI Link integrity verification"
" IE length (%u)\n", dev->name, skb->data[i]);
return 1; return 1;
} }
i++; i++;
i++; /* Skip length field */
hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
rxseq = skb->data[i++]; /* Should confirm our sequence */ rxseq = skb->data[i++]; /* Should confirm our sequence */
txseq = hdlc->state.fr.txseq; txseq = hdlc->state.fr.txseq;
if (hdlc->state.fr.settings.dce) { if (dce)
if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
printk(KERN_INFO "%s: Unsupported report type=%x\n",
dev->name, reptype);
return 1;
}
hdlc->state.fr.last_poll = jiffies; hdlc->state.fr.last_poll = jiffies;
}
error = 0; error = 0;
if (!hdlc->state.fr.reliable) if (!hdlc->state.fr.reliable)
@ -703,7 +727,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
error = 1; error = 1;
} }
if (hdlc->state.fr.settings.dce) { if (dce) {
if (hdlc->state.fr.fullrep_sent && !error) { if (hdlc->state.fr.fullrep_sent && !error) {
/* Stop sending full report - the last one has been confirmed by DTE */ /* Stop sending full report - the last one has been confirmed by DTE */
hdlc->state.fr.fullrep_sent = 0; hdlc->state.fr.fullrep_sent = 0;
@ -725,6 +749,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
hdlc->state.fr.dce_changed = 0; hdlc->state.fr.dce_changed = 0;
} }
hdlc->state.fr.request = 1; /* got request */
fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0); fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
return 0; return 0;
} }
@ -739,7 +764,6 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (reptype != LMI_FULLREP) if (reptype != LMI_FULLREP)
return 0; return 0;
stat_len = 3;
pvc = hdlc->state.fr.first_pvc; pvc = hdlc->state.fr.first_pvc;
while (pvc) { while (pvc) {
@ -750,24 +774,35 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
no_ram = 0; no_ram = 0;
while (skb->len >= i + 2 + stat_len) { while (skb->len >= i + 2 + stat_len) {
u16 dlci; u16 dlci;
u32 bw;
unsigned int active, new; unsigned int active, new;
if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :
? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { LMI_ANSI_CISCO_PVCSTAT)) {
printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", printk(KERN_INFO "%s: Not an LMI PVC status IE"
dev->name, skb->data[i]); " (0x%02X)\n", dev->name, skb->data[i]);
return 1;
}
if (skb->data[++i] != stat_len) {
printk(KERN_INFO "%s: Invalid LMI PVC status IE length"
" (%u)\n", dev->name, skb->data[i]);
return 1; return 1;
} }
i++; i++;
if (skb->data[i] != stat_len) { new = !! (skb->data[i + 2] & 0x08);
printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", active = !! (skb->data[i + 2] & 0x02);
dev->name, skb->data[i]); if (lmi == LMI_CISCO) {
return 1; dlci = (skb->data[i] << 8) | skb->data[i + 1];
bw = (skb->data[i + 3] << 16) |
(skb->data[i + 4] << 8) |
(skb->data[i + 5]);
} else {
dlci = ((skb->data[i] & 0x3F) << 4) |
((skb->data[i + 1] & 0x78) >> 3);
bw = 0;
} }
i++;
dlci = status_to_dlci(skb->data + i, &active, &new);
pvc = add_pvc(dev, dlci); pvc = add_pvc(dev, dlci);
@ -783,9 +818,11 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
pvc->state.deleted = 0; pvc->state.deleted = 0;
if (active != pvc->state.active || if (active != pvc->state.active ||
new != pvc->state.new || new != pvc->state.new ||
bw != pvc->state.bandwidth ||
!pvc->state.exist) { !pvc->state.exist) {
pvc->state.new = new; pvc->state.new = new;
pvc->state.active = active; pvc->state.active = active;
pvc->state.bandwidth = bw;
pvc_carrier(active, pvc); pvc_carrier(active, pvc);
fr_log_dlci_active(pvc); fr_log_dlci_active(pvc);
} }
@ -801,6 +838,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
pvc_carrier(0, pvc); pvc_carrier(0, pvc);
pvc->state.active = pvc->state.new = 0; pvc->state.active = pvc->state.new = 0;
pvc->state.exist = 0; pvc->state.exist = 0;
pvc->state.bandwidth = 0;
fr_log_dlci_active(pvc); fr_log_dlci_active(pvc);
} }
pvc = pvc->next; pvc = pvc->next;
@ -829,22 +867,15 @@ static int fr_rx(struct sk_buff *skb)
dlci = q922_to_dlci(skb->data); dlci = q922_to_dlci(skb->data);
if (dlci == LMI_DLCI) { if ((dlci == LMI_CCITT_ANSI_DLCI &&
if (hdlc->state.fr.settings.lmi == LMI_NONE) (hdlc->state.fr.settings.lmi == LMI_ANSI ||
goto rx_error; /* LMI packet with no LMI? */ hdlc->state.fr.settings.lmi == LMI_CCITT)) ||
(dlci == LMI_CISCO_DLCI &&
if (data[3] == LMI_PROTO) { hdlc->state.fr.settings.lmi == LMI_CISCO)) {
if (fr_lmi_recv(ndev, skb)) if (fr_lmi_recv(ndev, skb))
goto rx_error; goto rx_error;
else { dev_kfree_skb_any(skb);
dev_kfree_skb_any(skb); return NET_RX_SUCCESS;
return NET_RX_SUCCESS;
}
}
printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
ndev->name);
goto rx_error;
} }
pvc = find_pvc(hdlc, dlci); pvc = find_pvc(hdlc, dlci);
@ -1170,7 +1201,8 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
if ((new_settings.lmi != LMI_NONE && if ((new_settings.lmi != LMI_NONE &&
new_settings.lmi != LMI_ANSI && new_settings.lmi != LMI_ANSI &&
new_settings.lmi != LMI_CCITT) || new_settings.lmi != LMI_CCITT &&
new_settings.lmi != LMI_CISCO) ||
new_settings.t391 < 1 || new_settings.t391 < 1 ||
new_settings.t392 < 2 || new_settings.t392 < 2 ||
new_settings.n391 < 1 || new_settings.n391 < 1 ||

View File

@ -1,7 +1,7 @@
/* /*
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* *
* Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl> * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of version 2 of the GNU General Public License
@ -38,7 +38,7 @@
#include <linux/hdlc.h> #include <linux/hdlc.h>
static const char* version = "HDLC support module revision 1.17"; static const char* version = "HDLC support module revision 1.18";
#undef DEBUG_LINK #undef DEBUG_LINK
@ -126,10 +126,13 @@ void hdlc_set_carrier(int on, struct net_device *dev)
if (!hdlc->open) if (!hdlc->open)
goto carrier_exit; goto carrier_exit;
if (hdlc->carrier) if (hdlc->carrier) {
printk(KERN_INFO "%s: Carrier detected\n", dev->name);
__hdlc_set_carrier_on(dev); __hdlc_set_carrier_on(dev);
else } else {
printk(KERN_INFO "%s: Carrier lost\n", dev->name);
__hdlc_set_carrier_off(dev); __hdlc_set_carrier_off(dev);
}
carrier_exit: carrier_exit:
spin_unlock_irqrestore(&hdlc->state_lock, flags); spin_unlock_irqrestore(&hdlc->state_lock, flags);
@ -157,8 +160,11 @@ int hdlc_open(struct net_device *dev)
spin_lock_irq(&hdlc->state_lock); spin_lock_irq(&hdlc->state_lock);
if (hdlc->carrier) if (hdlc->carrier) {
printk(KERN_INFO "%s: Carrier detected\n", dev->name);
__hdlc_set_carrier_on(dev); __hdlc_set_carrier_on(dev);
} else
printk(KERN_INFO "%s: No carrier\n", dev->name);
hdlc->open = 1; hdlc->open = 1;

View File

@ -1,7 +1,7 @@
/* /*
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* *
* Copyright (C) 1999-2003 Krzysztof Halasa <khc@pm.waw.pl> * Copyright (C) 1999-2005 Krzysztof Halasa <khc@pm.waw.pl>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of version 2 of the GNU General Public License
@ -41,6 +41,7 @@
#define LMI_NONE 1 /* No LMI, all PVCs are static */ #define LMI_NONE 1 /* No LMI, all PVCs are static */
#define LMI_ANSI 2 /* ANSI Annex D */ #define LMI_ANSI 2 /* ANSI Annex D */
#define LMI_CCITT 3 /* ITU-T Annex A */ #define LMI_CCITT 3 /* ITU-T Annex A */
#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */
#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ #define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */
#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */ #define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
@ -89,6 +90,7 @@ typedef struct pvc_device_struct {
unsigned int deleted: 1; unsigned int deleted: 1;
unsigned int fecn: 1; unsigned int fecn: 1;
unsigned int becn: 1; unsigned int becn: 1;
unsigned int bandwidth; /* Cisco LMI reporting only */
}state; }state;
}pvc_device; }pvc_device;