[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:
parent
88d7bd8cb9
commit
b3dd65f958
|
@ -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/>.
|
||||||
|
|
|
@ -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 ||
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue