diff options
Diffstat (limited to 'drivers/net/wan/hdlc_fr.c')
-rw-r--r-- | drivers/net/wan/hdlc_fr.c | 320 |
1 files changed, 176 insertions, 144 deletions
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 7f450b51a6cb..a5d6891c9d4c 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Generic HDLC support routines for Linux | 2 | * Generic HDLC support routines for Linux |
3 | * Frame Relay support | 3 | * Frame Relay support |
4 | * | 4 | * |
5 | * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl> | 5 | * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of version 2 of the GNU General Public License | 8 | * under the terms of version 2 of the GNU General Public License |
@@ -27,6 +27,10 @@ | |||
27 | active = open and "link reliable" | 27 | active = open and "link reliable" |
28 | exist = new = not used | 28 | exist = new = not used |
29 | 29 | ||
30 | CCITT LMI: ITU-T Q.933 Annex A | ||
31 | ANSI LMI: ANSI T1.617 Annex D | ||
32 | CISCO LMI: the original, aka "Gang of Four" LMI | ||
33 | |||
30 | */ | 34 | */ |
31 | 35 | ||
32 | #include <linux/module.h> | 36 | #include <linux/module.h> |
@@ -49,45 +53,41 @@ | |||
49 | #undef DEBUG_ECN | 53 | #undef DEBUG_ECN |
50 | #undef DEBUG_LINK | 54 | #undef DEBUG_LINK |
51 | 55 | ||
52 | #define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */ | 56 | #define FR_UI 0x03 |
53 | 57 | #define FR_PAD 0x00 | |
54 | #define PVC_STATE_NEW 0x01 | 58 | |
55 | #define PVC_STATE_ACTIVE 0x02 | 59 | #define NLPID_IP 0xCC |
56 | #define PVC_STATE_FECN 0x08 /* FECN condition */ | 60 | #define NLPID_IPV6 0x8E |
57 | #define PVC_STATE_BECN 0x10 /* BECN condition */ | 61 | #define NLPID_SNAP 0x80 |
58 | 62 | #define NLPID_PAD 0x00 | |
59 | 63 | #define NLPID_CCITT_ANSI_LMI 0x08 | |
60 | #define FR_UI 0x03 | 64 | #define NLPID_CISCO_LMI 0x09 |
61 | #define FR_PAD 0x00 | 65 | |
62 | 66 | ||
63 | #define NLPID_IP 0xCC | 67 | #define LMI_CCITT_ANSI_DLCI 0 /* LMI DLCI */ |
64 | #define NLPID_IPV6 0x8E | 68 | #define LMI_CISCO_DLCI 1023 |
65 | #define NLPID_SNAP 0x80 | 69 | |
66 | #define NLPID_PAD 0x00 | 70 | #define LMI_CALLREF 0x00 /* Call Reference */ |
67 | #define NLPID_Q933 0x08 | 71 | #define LMI_ANSI_LOCKSHIFT 0x95 /* ANSI locking shift */ |
68 | 72 | #define LMI_ANSI_CISCO_REPTYPE 0x01 /* report type */ | |
69 | 73 | #define LMI_CCITT_REPTYPE 0x51 | |
70 | #define LMI_DLCI 0 /* LMI DLCI */ | 74 | #define LMI_ANSI_CISCO_ALIVE 0x03 /* keep alive */ |
71 | #define LMI_PROTO 0x08 | 75 | #define LMI_CCITT_ALIVE 0x53 |
72 | #define LMI_CALLREF 0x00 /* Call Reference */ | 76 | #define LMI_ANSI_CISCO_PVCSTAT 0x07 /* PVC status */ |
73 | #define LMI_ANSI_LOCKSHIFT 0x95 /* ANSI lockshift */ | 77 | #define LMI_CCITT_PVCSTAT 0x57 |
74 | #define LMI_REPTYPE 1 /* report type */ | 78 | |
75 | #define LMI_CCITT_REPTYPE 0x51 | 79 | #define LMI_FULLREP 0x00 /* full report */ |
76 | #define LMI_ALIVE 3 /* keep alive */ | 80 | #define LMI_INTEGRITY 0x01 /* link integrity report */ |
77 | #define LMI_CCITT_ALIVE 0x53 | 81 | #define LMI_SINGLE 0x02 /* single PVC report */ |
78 | #define LMI_PVCSTAT 7 /* pvc status */ | 82 | |
79 | #define LMI_CCITT_PVCSTAT 0x57 | ||
80 | #define LMI_FULLREP 0 /* full report */ | ||
81 | #define LMI_INTEGRITY 1 /* link integrity report */ | ||
82 | #define LMI_SINGLE 2 /* single pvc report */ | ||
83 | #define LMI_STATUS_ENQUIRY 0x75 | 83 | #define LMI_STATUS_ENQUIRY 0x75 |
84 | #define LMI_STATUS 0x7D /* reply */ | 84 | #define LMI_STATUS 0x7D /* reply */ |
85 | 85 | ||
86 | #define LMI_REPT_LEN 1 /* report type element length */ | 86 | #define LMI_REPT_LEN 1 /* report type element length */ |
87 | #define LMI_INTEG_LEN 2 /* link integrity element length */ | 87 | #define LMI_INTEG_LEN 2 /* link integrity element length */ |
88 | 88 | ||
89 | #define LMI_LENGTH 13 /* standard LMI frame length */ | 89 | #define LMI_CCITT_CISCO_LENGTH 13 /* LMI frame lengths */ |
90 | #define LMI_ANSI_LENGTH 14 | 90 | #define LMI_ANSI_LENGTH 14 |
91 | 91 | ||
92 | 92 | ||
93 | typedef struct { | 93 | typedef struct { |
@@ -223,51 +223,34 @@ static inline struct net_device** get_dev_p(pvc_device *pvc, int type) | |||
223 | } | 223 | } |
224 | 224 | ||
225 | 225 | ||
226 | static inline u16 status_to_dlci(u8 *status, int *active, int *new) | ||
227 | { | ||
228 | *new = (status[2] & 0x08) ? 1 : 0; | ||
229 | *active = (status[2] & 0x02) ? 1 : 0; | ||
230 | |||
231 | return ((status[0] & 0x3F) << 4) | ((status[1] & 0x78) >> 3); | ||
232 | } | ||
233 | |||
234 | |||
235 | static inline void dlci_to_status(u16 dlci, u8 *status, int active, int new) | ||
236 | { | ||
237 | status[0] = (dlci >> 4) & 0x3F; | ||
238 | status[1] = ((dlci << 3) & 0x78) | 0x80; | ||
239 | status[2] = 0x80; | ||
240 | |||
241 | if (new) | ||
242 | status[2] |= 0x08; | ||
243 | else if (active) | ||
244 | status[2] |= 0x02; | ||
245 | } | ||
246 | |||
247 | |||
248 | |||
249 | static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) | 226 | static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) |
250 | { | 227 | { |
251 | u16 head_len; | 228 | u16 head_len; |
252 | struct sk_buff *skb = *skb_p; | 229 | struct sk_buff *skb = *skb_p; |
253 | 230 | ||
254 | switch (skb->protocol) { | 231 | switch (skb->protocol) { |
255 | case __constant_ntohs(ETH_P_IP): | 232 | case __constant_ntohs(NLPID_CCITT_ANSI_LMI): |
256 | head_len = 4; | 233 | head_len = 4; |
257 | skb_push(skb, head_len); | 234 | skb_push(skb, head_len); |
258 | skb->data[3] = NLPID_IP; | 235 | skb->data[3] = NLPID_CCITT_ANSI_LMI; |
259 | break; | 236 | break; |
260 | 237 | ||
261 | case __constant_ntohs(ETH_P_IPV6): | 238 | case __constant_ntohs(NLPID_CISCO_LMI): |
262 | head_len = 4; | 239 | head_len = 4; |
263 | skb_push(skb, head_len); | 240 | skb_push(skb, head_len); |
264 | skb->data[3] = NLPID_IPV6; | 241 | skb->data[3] = NLPID_CISCO_LMI; |
265 | break; | 242 | break; |
266 | 243 | ||
267 | case __constant_ntohs(LMI_PROTO): | 244 | case __constant_ntohs(ETH_P_IP): |
245 | head_len = 4; | ||
246 | skb_push(skb, head_len); | ||
247 | skb->data[3] = NLPID_IP; | ||
248 | break; | ||
249 | |||
250 | case __constant_ntohs(ETH_P_IPV6): | ||
268 | head_len = 4; | 251 | head_len = 4; |
269 | skb_push(skb, head_len); | 252 | skb_push(skb, head_len); |
270 | skb->data[3] = LMI_PROTO; | 253 | skb->data[3] = NLPID_IPV6; |
271 | break; | 254 | break; |
272 | 255 | ||
273 | case __constant_ntohs(ETH_P_802_3): | 256 | case __constant_ntohs(ETH_P_802_3): |
@@ -461,13 +444,14 @@ static void fr_lmi_send(struct net_device *dev, int fullrep) | |||
461 | hdlc_device *hdlc = dev_to_hdlc(dev); | 444 | hdlc_device *hdlc = dev_to_hdlc(dev); |
462 | struct sk_buff *skb; | 445 | struct sk_buff *skb; |
463 | pvc_device *pvc = hdlc->state.fr.first_pvc; | 446 | pvc_device *pvc = hdlc->state.fr.first_pvc; |
464 | int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH | 447 | int lmi = hdlc->state.fr.settings.lmi; |
465 | : LMI_LENGTH; | 448 | int dce = hdlc->state.fr.settings.dce; |
466 | int stat_len = 3; | 449 | int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH; |
450 | int stat_len = (lmi == LMI_CISCO) ? 6 : 3; | ||
467 | u8 *data; | 451 | u8 *data; |
468 | int i = 0; | 452 | int i = 0; |
469 | 453 | ||
470 | if (hdlc->state.fr.settings.dce && fullrep) { | 454 | if (dce && fullrep) { |
471 | len += hdlc->state.fr.dce_pvc_count * (2 + stat_len); | 455 | len += hdlc->state.fr.dce_pvc_count * (2 + stat_len); |
472 | if (len > HDLC_MAX_MRU) { | 456 | if (len > HDLC_MAX_MRU) { |
473 | printk(KERN_WARNING "%s: Too many PVCs while sending " | 457 | printk(KERN_WARNING "%s: Too many PVCs while sending " |
@@ -484,29 +468,31 @@ static void fr_lmi_send(struct net_device *dev, int fullrep) | |||
484 | } | 468 | } |
485 | memset(skb->data, 0, len); | 469 | memset(skb->data, 0, len); |
486 | skb_reserve(skb, 4); | 470 | skb_reserve(skb, 4); |
487 | skb->protocol = __constant_htons(LMI_PROTO); | 471 | if (lmi == LMI_CISCO) { |
488 | fr_hard_header(&skb, LMI_DLCI); | 472 | skb->protocol = __constant_htons(NLPID_CISCO_LMI); |
473 | fr_hard_header(&skb, LMI_CISCO_DLCI); | ||
474 | } else { | ||
475 | skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI); | ||
476 | fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI); | ||
477 | } | ||
489 | data = skb->tail; | 478 | data = skb->tail; |
490 | data[i++] = LMI_CALLREF; | 479 | data[i++] = LMI_CALLREF; |
491 | data[i++] = hdlc->state.fr.settings.dce | 480 | data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY; |
492 | ? LMI_STATUS : LMI_STATUS_ENQUIRY; | 481 | if (lmi == LMI_ANSI) |
493 | if (hdlc->state.fr.settings.lmi == LMI_ANSI) | ||
494 | data[i++] = LMI_ANSI_LOCKSHIFT; | 482 | data[i++] = LMI_ANSI_LOCKSHIFT; |
495 | data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) | 483 | data[i++] = lmi == LMI_CCITT ? LMI_CCITT_REPTYPE : |
496 | ? LMI_CCITT_REPTYPE : LMI_REPTYPE; | 484 | LMI_ANSI_CISCO_REPTYPE; |
497 | data[i++] = LMI_REPT_LEN; | 485 | data[i++] = LMI_REPT_LEN; |
498 | data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; | 486 | data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; |
499 | 487 | data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE; | |
500 | data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) | ||
501 | ? LMI_CCITT_ALIVE : LMI_ALIVE; | ||
502 | data[i++] = LMI_INTEG_LEN; | 488 | data[i++] = LMI_INTEG_LEN; |
503 | data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); | 489 | data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq); |
504 | data[i++] = hdlc->state.fr.rxseq; | 490 | data[i++] = hdlc->state.fr.rxseq; |
505 | 491 | ||
506 | if (hdlc->state.fr.settings.dce && fullrep) { | 492 | if (dce && fullrep) { |
507 | while (pvc) { | 493 | while (pvc) { |
508 | data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT) | 494 | data[i++] = lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT : |
509 | ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; | 495 | LMI_ANSI_CISCO_PVCSTAT; |
510 | data[i++] = stat_len; | 496 | data[i++] = stat_len; |
511 | 497 | ||
512 | /* LMI start/restart */ | 498 | /* LMI start/restart */ |
@@ -523,8 +509,20 @@ static void fr_lmi_send(struct net_device *dev, int fullrep) | |||
523 | fr_log_dlci_active(pvc); | 509 | fr_log_dlci_active(pvc); |
524 | } | 510 | } |
525 | 511 | ||
526 | dlci_to_status(pvc->dlci, data + i, | 512 | if (lmi == LMI_CISCO) { |
527 | pvc->state.active, pvc->state.new); | 513 | data[i] = pvc->dlci >> 8; |
514 | data[i + 1] = pvc->dlci & 0xFF; | ||
515 | } else { | ||
516 | data[i] = (pvc->dlci >> 4) & 0x3F; | ||
517 | data[i + 1] = ((pvc->dlci << 3) & 0x78) | 0x80; | ||
518 | data[i + 2] = 0x80; | ||
519 | } | ||
520 | |||
521 | if (pvc->state.new) | ||
522 | data[i + 2] |= 0x08; | ||
523 | else if (pvc->state.active) | ||
524 | data[i + 2] |= 0x02; | ||
525 | |||
528 | i += stat_len; | 526 | i += stat_len; |
529 | pvc = pvc->next; | 527 | pvc = pvc->next; |
530 | } | 528 | } |
@@ -569,6 +567,8 @@ static void fr_set_link_state(int reliable, struct net_device *dev) | |||
569 | pvc_carrier(0, pvc); | 567 | pvc_carrier(0, pvc); |
570 | pvc->state.exist = pvc->state.active = 0; | 568 | pvc->state.exist = pvc->state.active = 0; |
571 | pvc->state.new = 0; | 569 | pvc->state.new = 0; |
570 | if (!hdlc->state.fr.settings.dce) | ||
571 | pvc->state.bandwidth = 0; | ||
572 | pvc = pvc->next; | 572 | pvc = pvc->next; |
573 | } | 573 | } |
574 | } | 574 | } |
@@ -583,11 +583,12 @@ static void fr_timer(unsigned long arg) | |||
583 | int i, cnt = 0, reliable; | 583 | int i, cnt = 0, reliable; |
584 | u32 list; | 584 | u32 list; |
585 | 585 | ||
586 | if (hdlc->state.fr.settings.dce) | 586 | if (hdlc->state.fr.settings.dce) { |
587 | reliable = hdlc->state.fr.request && | 587 | reliable = hdlc->state.fr.request && |
588 | time_before(jiffies, hdlc->state.fr.last_poll + | 588 | time_before(jiffies, hdlc->state.fr.last_poll + |
589 | hdlc->state.fr.settings.t392 * HZ); | 589 | hdlc->state.fr.settings.t392 * HZ); |
590 | else { | 590 | hdlc->state.fr.request = 0; |
591 | } else { | ||
591 | hdlc->state.fr.last_errors <<= 1; /* Shift the list */ | 592 | hdlc->state.fr.last_errors <<= 1; /* Shift the list */ |
592 | if (hdlc->state.fr.request) { | 593 | if (hdlc->state.fr.request) { |
593 | if (hdlc->state.fr.reliable) | 594 | if (hdlc->state.fr.reliable) |
@@ -634,65 +635,88 @@ static void fr_timer(unsigned long arg) | |||
634 | static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | 635 | static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) |
635 | { | 636 | { |
636 | hdlc_device *hdlc = dev_to_hdlc(dev); | 637 | hdlc_device *hdlc = dev_to_hdlc(dev); |
637 | int stat_len; | ||
638 | pvc_device *pvc; | 638 | pvc_device *pvc; |
639 | int reptype = -1, error, no_ram; | ||
640 | u8 rxseq, txseq; | 639 | u8 rxseq, txseq; |
641 | int i; | 640 | int lmi = hdlc->state.fr.settings.lmi; |
641 | int dce = hdlc->state.fr.settings.dce; | ||
642 | int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i; | ||
642 | 643 | ||
643 | if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI) | 644 | if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH : |
644 | ? LMI_ANSI_LENGTH : LMI_LENGTH)) { | 645 | LMI_CCITT_CISCO_LENGTH)) { |
645 | printk(KERN_INFO "%s: Short LMI frame\n", dev->name); | 646 | printk(KERN_INFO "%s: Short LMI frame\n", dev->name); |
646 | return 1; | 647 | return 1; |
647 | } | 648 | } |
648 | 649 | ||
649 | if (skb->data[5] != (!hdlc->state.fr.settings.dce ? | 650 | if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI : |
650 | LMI_STATUS : LMI_STATUS_ENQUIRY)) { | 651 | NLPID_CCITT_ANSI_LMI)) { |
651 | printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", | 652 | printk(KERN_INFO "%s: Received non-LMI frame with LMI" |
652 | dev->name, skb->data[2], | 653 | " DLCI\n", dev->name); |
653 | hdlc->state.fr.settings.dce ? "enquiry" : "reply"); | 654 | return 1; |
655 | } | ||
656 | |||
657 | if (skb->data[4] != LMI_CALLREF) { | ||
658 | printk(KERN_INFO "%s: Invalid LMI Call reference (0x%02X)\n", | ||
659 | dev->name, skb->data[4]); | ||
660 | return 1; | ||
661 | } | ||
662 | |||
663 | if (skb->data[5] != (dce ? LMI_STATUS_ENQUIRY : LMI_STATUS)) { | ||
664 | printk(KERN_INFO "%s: Invalid LMI Message type (0x%02X)\n", | ||
665 | dev->name, skb->data[5]); | ||
654 | return 1; | 666 | return 1; |
655 | } | 667 | } |
656 | 668 | ||
657 | i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6; | 669 | if (lmi == LMI_ANSI) { |
670 | if (skb->data[6] != LMI_ANSI_LOCKSHIFT) { | ||
671 | printk(KERN_INFO "%s: Not ANSI locking shift in LMI" | ||
672 | " message (0x%02X)\n", dev->name, skb->data[6]); | ||
673 | return 1; | ||
674 | } | ||
675 | i = 7; | ||
676 | } else | ||
677 | i = 6; | ||
658 | 678 | ||
659 | if (skb->data[i] != | 679 | if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_REPTYPE : |
660 | ((hdlc->state.fr.settings.lmi == LMI_CCITT) | 680 | LMI_ANSI_CISCO_REPTYPE)) { |
661 | ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { | 681 | printk(KERN_INFO "%s: Not an LMI Report type IE (0x%02X)\n", |
662 | printk(KERN_INFO "%s: Not a report type=%x\n", | ||
663 | dev->name, skb->data[i]); | 682 | dev->name, skb->data[i]); |
664 | return 1; | 683 | return 1; |
665 | } | 684 | } |
666 | i++; | ||
667 | 685 | ||
668 | i++; /* Skip length field */ | 686 | if (skb->data[++i] != LMI_REPT_LEN) { |
687 | printk(KERN_INFO "%s: Invalid LMI Report type IE length" | ||
688 | " (%u)\n", dev->name, skb->data[i]); | ||
689 | return 1; | ||
690 | } | ||
669 | 691 | ||
670 | reptype = skb->data[i++]; | 692 | reptype = skb->data[++i]; |
693 | if (reptype != LMI_INTEGRITY && reptype != LMI_FULLREP) { | ||
694 | printk(KERN_INFO "%s: Unsupported LMI Report type (0x%02X)\n", | ||
695 | dev->name, reptype); | ||
696 | return 1; | ||
697 | } | ||
671 | 698 | ||
672 | if (skb->data[i]!= | 699 | if (skb->data[++i] != (lmi == LMI_CCITT ? LMI_CCITT_ALIVE : |
673 | ((hdlc->state.fr.settings.lmi == LMI_CCITT) | 700 | LMI_ANSI_CISCO_ALIVE)) { |
674 | ? LMI_CCITT_ALIVE : LMI_ALIVE)) { | 701 | printk(KERN_INFO "%s: Not an LMI Link integrity verification" |
675 | printk(KERN_INFO "%s: Unsupported status element=%x\n", | 702 | " IE (0x%02X)\n", dev->name, skb->data[i]); |
676 | dev->name, skb->data[i]); | ||
677 | return 1; | 703 | return 1; |
678 | } | 704 | } |
679 | i++; | ||
680 | 705 | ||
681 | i++; /* Skip length field */ | 706 | if (skb->data[++i] != LMI_INTEG_LEN) { |
707 | printk(KERN_INFO "%s: Invalid LMI Link integrity verification" | ||
708 | " IE length (%u)\n", dev->name, skb->data[i]); | ||
709 | return 1; | ||
710 | } | ||
711 | i++; | ||
682 | 712 | ||
683 | hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ | 713 | hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */ |
684 | rxseq = skb->data[i++]; /* Should confirm our sequence */ | 714 | rxseq = skb->data[i++]; /* Should confirm our sequence */ |
685 | 715 | ||
686 | txseq = hdlc->state.fr.txseq; | 716 | txseq = hdlc->state.fr.txseq; |
687 | 717 | ||
688 | if (hdlc->state.fr.settings.dce) { | 718 | if (dce) |
689 | if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { | ||
690 | printk(KERN_INFO "%s: Unsupported report type=%x\n", | ||
691 | dev->name, reptype); | ||
692 | return 1; | ||
693 | } | ||
694 | hdlc->state.fr.last_poll = jiffies; | 719 | hdlc->state.fr.last_poll = jiffies; |
695 | } | ||
696 | 720 | ||
697 | error = 0; | 721 | error = 0; |
698 | if (!hdlc->state.fr.reliable) | 722 | if (!hdlc->state.fr.reliable) |
@@ -703,7 +727,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
703 | error = 1; | 727 | error = 1; |
704 | } | 728 | } |
705 | 729 | ||
706 | if (hdlc->state.fr.settings.dce) { | 730 | if (dce) { |
707 | if (hdlc->state.fr.fullrep_sent && !error) { | 731 | if (hdlc->state.fr.fullrep_sent && !error) { |
708 | /* Stop sending full report - the last one has been confirmed by DTE */ | 732 | /* Stop sending full report - the last one has been confirmed by DTE */ |
709 | hdlc->state.fr.fullrep_sent = 0; | 733 | hdlc->state.fr.fullrep_sent = 0; |
@@ -725,6 +749,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
725 | hdlc->state.fr.dce_changed = 0; | 749 | hdlc->state.fr.dce_changed = 0; |
726 | } | 750 | } |
727 | 751 | ||
752 | hdlc->state.fr.request = 1; /* got request */ | ||
728 | fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0); | 753 | fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0); |
729 | return 0; | 754 | return 0; |
730 | } | 755 | } |
@@ -739,7 +764,6 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
739 | if (reptype != LMI_FULLREP) | 764 | if (reptype != LMI_FULLREP) |
740 | return 0; | 765 | return 0; |
741 | 766 | ||
742 | stat_len = 3; | ||
743 | pvc = hdlc->state.fr.first_pvc; | 767 | pvc = hdlc->state.fr.first_pvc; |
744 | 768 | ||
745 | while (pvc) { | 769 | while (pvc) { |
@@ -750,24 +774,35 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
750 | no_ram = 0; | 774 | no_ram = 0; |
751 | while (skb->len >= i + 2 + stat_len) { | 775 | while (skb->len >= i + 2 + stat_len) { |
752 | u16 dlci; | 776 | u16 dlci; |
777 | u32 bw; | ||
753 | unsigned int active, new; | 778 | unsigned int active, new; |
754 | 779 | ||
755 | if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) | 780 | if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT : |
756 | ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { | 781 | LMI_ANSI_CISCO_PVCSTAT)) { |
757 | printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", | 782 | printk(KERN_INFO "%s: Not an LMI PVC status IE" |
758 | dev->name, skb->data[i]); | 783 | " (0x%02X)\n", dev->name, skb->data[i]); |
759 | return 1; | 784 | return 1; |
760 | } | 785 | } |
761 | i++; | ||
762 | 786 | ||
763 | if (skb->data[i] != stat_len) { | 787 | if (skb->data[++i] != stat_len) { |
764 | printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", | 788 | printk(KERN_INFO "%s: Invalid LMI PVC status IE length" |
765 | dev->name, skb->data[i]); | 789 | " (%u)\n", dev->name, skb->data[i]); |
766 | return 1; | 790 | return 1; |
767 | } | 791 | } |
768 | i++; | 792 | i++; |
769 | 793 | ||
770 | dlci = status_to_dlci(skb->data + i, &active, &new); | 794 | new = !! (skb->data[i + 2] & 0x08); |
795 | active = !! (skb->data[i + 2] & 0x02); | ||
796 | if (lmi == LMI_CISCO) { | ||
797 | dlci = (skb->data[i] << 8) | skb->data[i + 1]; | ||
798 | bw = (skb->data[i + 3] << 16) | | ||
799 | (skb->data[i + 4] << 8) | | ||
800 | (skb->data[i + 5]); | ||
801 | } else { | ||
802 | dlci = ((skb->data[i] & 0x3F) << 4) | | ||
803 | ((skb->data[i + 1] & 0x78) >> 3); | ||
804 | bw = 0; | ||
805 | } | ||
771 | 806 | ||
772 | pvc = add_pvc(dev, dlci); | 807 | pvc = add_pvc(dev, dlci); |
773 | 808 | ||
@@ -783,9 +818,11 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
783 | pvc->state.deleted = 0; | 818 | pvc->state.deleted = 0; |
784 | if (active != pvc->state.active || | 819 | if (active != pvc->state.active || |
785 | new != pvc->state.new || | 820 | new != pvc->state.new || |
821 | bw != pvc->state.bandwidth || | ||
786 | !pvc->state.exist) { | 822 | !pvc->state.exist) { |
787 | pvc->state.new = new; | 823 | pvc->state.new = new; |
788 | pvc->state.active = active; | 824 | pvc->state.active = active; |
825 | pvc->state.bandwidth = bw; | ||
789 | pvc_carrier(active, pvc); | 826 | pvc_carrier(active, pvc); |
790 | fr_log_dlci_active(pvc); | 827 | fr_log_dlci_active(pvc); |
791 | } | 828 | } |
@@ -801,6 +838,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb) | |||
801 | pvc_carrier(0, pvc); | 838 | pvc_carrier(0, pvc); |
802 | pvc->state.active = pvc->state.new = 0; | 839 | pvc->state.active = pvc->state.new = 0; |
803 | pvc->state.exist = 0; | 840 | pvc->state.exist = 0; |
841 | pvc->state.bandwidth = 0; | ||
804 | fr_log_dlci_active(pvc); | 842 | fr_log_dlci_active(pvc); |
805 | } | 843 | } |
806 | pvc = pvc->next; | 844 | pvc = pvc->next; |
@@ -829,22 +867,15 @@ static int fr_rx(struct sk_buff *skb) | |||
829 | 867 | ||
830 | dlci = q922_to_dlci(skb->data); | 868 | dlci = q922_to_dlci(skb->data); |
831 | 869 | ||
832 | if (dlci == LMI_DLCI) { | 870 | if ((dlci == LMI_CCITT_ANSI_DLCI && |
833 | if (hdlc->state.fr.settings.lmi == LMI_NONE) | 871 | (hdlc->state.fr.settings.lmi == LMI_ANSI || |
834 | goto rx_error; /* LMI packet with no LMI? */ | 872 | hdlc->state.fr.settings.lmi == LMI_CCITT)) || |
835 | 873 | (dlci == LMI_CISCO_DLCI && | |
836 | if (data[3] == LMI_PROTO) { | 874 | hdlc->state.fr.settings.lmi == LMI_CISCO)) { |
837 | if (fr_lmi_recv(ndev, skb)) | 875 | if (fr_lmi_recv(ndev, skb)) |
838 | goto rx_error; | 876 | goto rx_error; |
839 | else { | 877 | dev_kfree_skb_any(skb); |
840 | dev_kfree_skb_any(skb); | 878 | return NET_RX_SUCCESS; |
841 | return NET_RX_SUCCESS; | ||
842 | } | ||
843 | } | ||
844 | |||
845 | printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n", | ||
846 | ndev->name); | ||
847 | goto rx_error; | ||
848 | } | 879 | } |
849 | 880 | ||
850 | pvc = find_pvc(hdlc, dlci); | 881 | pvc = find_pvc(hdlc, dlci); |
@@ -1170,7 +1201,8 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr) | |||
1170 | 1201 | ||
1171 | if ((new_settings.lmi != LMI_NONE && | 1202 | if ((new_settings.lmi != LMI_NONE && |
1172 | new_settings.lmi != LMI_ANSI && | 1203 | new_settings.lmi != LMI_ANSI && |
1173 | new_settings.lmi != LMI_CCITT) || | 1204 | new_settings.lmi != LMI_CCITT && |
1205 | new_settings.lmi != LMI_CISCO) || | ||
1174 | new_settings.t391 < 1 || | 1206 | new_settings.t391 < 1 || |
1175 | new_settings.t392 < 2 || | 1207 | new_settings.t392 < 2 || |
1176 | new_settings.n391 < 1 || | 1208 | new_settings.n391 < 1 || |