Have the Etherenet and PPP dissectors register themselves, and have
[obnox/wireshark/wip.git] / packet-ppp.c
1 /* packet-ppp.c
2  * Routines for ppp packet disassembly
3  *
4  * $Id: packet-ppp.c,v 1.42 2000/11/19 02:00:02 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  *
9  * This file created and by Mike Hall <mlh@io.com>
10  * Copyright 1998
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #include <glib.h>
36 #include "packet.h"
37 #include "packet-ppp.h"
38 #include "ppptypes.h"
39 #include "packet-atalk.h"
40 #include "packet-ip.h"
41 #include "packet-ipv6.h"
42 #include "packet-ipx.h"
43 #include "packet-vines.h"
44
45 static int proto_ppp = -1;
46
47 static gint ett_ppp = -1;
48 static gint ett_ipcp = -1;
49 static gint ett_ipcp_options = -1;
50 static gint ett_ipcp_ipaddrs_opt = -1;
51 static gint ett_ipcp_compressprot_opt = -1;
52 static gint ett_lcp = -1;
53 static gint ett_lcp_options = -1;
54 static gint ett_lcp_mru_opt = -1;
55 static gint ett_lcp_async_map_opt = -1;
56 static gint ett_lcp_authprot_opt = -1;
57 static gint ett_lcp_qualprot_opt = -1;
58 static gint ett_lcp_magicnum_opt = -1;
59 static gint ett_lcp_fcs_alternatives_opt = -1;
60 static gint ett_lcp_numbered_mode_opt = -1;
61 static gint ett_lcp_callback_opt = -1;
62 static gint ett_lcp_multilink_ep_disc_opt = -1;
63 static gint ett_lcp_internationalization_opt = -1;
64
65 static dissector_table_t subdissector_table;
66
67 static int proto_mp = -1;
68 static int hf_mp_frag_first = -1;
69 static int hf_mp_frag_last = -1;
70 static int hf_mp_sequence_num = -1;
71
72 static int ett_mp = -1;
73 static int ett_mp_flags = -1;
74
75 /* PPP structs and definitions */
76
77 typedef struct _e_ppphdr {
78   guint8  ppp_addr;
79   guint8  ppp_ctl;
80   guint16 ppp_prot;
81 } e_ppphdr;
82
83 static const value_string ppp_vals[] = {
84         {PPP_IP,        "IP"             },
85         {PPP_AT,        "Appletalk"      },
86         {PPP_IPX,       "Netware IPX/SPX"},
87         {PPP_VJC_COMP,  "VJ compressed TCP"},
88         {PPP_VJC_UNCOMP,"VJ uncompressed TCP"}, 
89         {PPP_VINES,     "Vines"          },
90         {PPP_MP,        "Multilink"},
91         {PPP_IPV6,      "IPv6"           },
92         {PPP_COMP,      "compressed packet" },
93         {PPP_IPCP,      "IP Control Protocol" },
94         {PPP_ATCP,      "AppleTalk Control Protocol" },
95         {PPP_IPXCP,     "IPX Control Protocol" },
96         {PPP_CCP,       "Compression Control Protocol" },
97         {PPP_LCP,       "Link Control Protocol" },
98         {PPP_PAP,       "Password Authentication Protocol"  },
99         {PPP_LQR,       "Link Quality Report protocol" },
100         {PPP_CHAP,      "Cryptographic Handshake Auth. Protocol" },
101         {PPP_CBCP,      "Callback Control Protocol" },
102         {0,             NULL            }
103 };
104
105 /* CP (LCP, IPCP, etc.) codes.
106  * from pppd fsm.h 
107  */
108 #define CONFREQ    1  /* Configuration Request */
109 #define CONFACK    2  /* Configuration Ack */
110 #define CONFNAK    3  /* Configuration Nak */
111 #define CONFREJ    4  /* Configuration Reject */
112 #define TERMREQ    5  /* Termination Request */
113 #define TERMACK    6  /* Termination Ack */
114 #define CODEREJ    7  /* Code Reject */
115
116 static const value_string cp_vals[] = {
117         {CONFREQ,    "Configuration Request" },
118         {CONFACK,    "Configuration Ack" },
119         {CONFNAK,    "Configuration Nak" },
120         {CONFREJ,    "Configuration Reject" },
121         {TERMREQ,    "Termination Request" },
122         {TERMACK,    "Termination Ack" },
123         {CODEREJ,    "Code Reject" },
124         {0,          NULL            } };
125
126 /*
127  * LCP-specific packet types.
128  */
129 #define PROTREJ    8  /* Protocol Reject */
130 #define ECHOREQ    9  /* Echo Request */
131 #define ECHOREP    10 /* Echo Reply */
132 #define DISCREQ    11 /* Discard Request */
133 #define IDENT      12 /* Identification */
134 #define TIMEREMAIN 13 /* Time remaining */
135
136 #define CBCP_OPT  6 /* Use callback control protocol */
137
138 static const value_string lcp_vals[] = {
139         {CONFREQ,    "Configuration Request" },
140         {CONFACK,    "Configuration Ack" },
141         {CONFNAK,    "Configuration Nak" },
142         {CONFREJ,    "Configuration Reject" },
143         {TERMREQ,    "Termination Request" },
144         {TERMACK,    "Termination Ack" },
145         {CODEREJ,    "Code Reject" },
146         {PROTREJ,    "Protocol Reject" },
147         {ECHOREQ,    "Echo Request" },
148         {ECHOREP,    "Echo Reply" },
149         {DISCREQ,    "Discard Request" },
150         {IDENT,      "Identification" },
151         {TIMEREMAIN, "Time Remaining" },
152         {0,          NULL }
153 };
154
155 /*
156  * Options.  (LCP)
157  */
158 #define CI_MRU                  1       /* Maximum Receive Unit */
159 #define CI_ASYNCMAP             2       /* Async Control Character Map */
160 #define CI_AUTHTYPE             3       /* Authentication Type */
161 #define CI_QUALITY              4       /* Quality Protocol */
162 #define CI_MAGICNUMBER          5       /* Magic Number */
163 #define CI_PCOMPRESSION         7       /* Protocol Field Compression */
164 #define CI_ACCOMPRESSION        8       /* Address/Control Field Compression */
165 #define CI_FCS_ALTERNATIVES     9       /* FCS Alternatives (RFC 1570) */
166 #define CI_SELF_DESCRIBING_PAD  10      /* Self-Describing Pad (RFC 1570) */
167 #define CI_NUMBERED_MODE        11      /* Numbered Mode (RFC 1663) */
168 #define CI_CALLBACK             13      /* Callback (RFC 1570) */
169 #define CI_COMPOUND_FRAMES      15      /* Compound frames (RFC 1570) */
170 #define CI_MULTILINK_MRRU       17      /* Multilink MRRU (RFC 1717) */
171 #define CI_MULTILINK_SSNH       18      /* Multilink Short Sequence Number
172                                            Header (RFC 1717) */
173 #define CI_MULTILINK_EP_DISC    19      /* Multilink Endpoint Discriminator
174                                            (RFC 1717) */
175 #define CI_DCE_IDENTIFIER       21      /* DCE Identifier */
176 #define CI_MULTILINK_PLUS_PROC  22      /* Multilink Plus Procedure */
177 #define CI_LINK_DISC_FOR_BACP   23      /* Link Discriminator for BACP
178                                            (RFC 2125) */
179 #define CI_LCP_AUTHENTICATION   24      /* LCP Authentication Option */
180 #define CI_COBS                 25      /* Consistent Overhead Byte
181                                            Stuffing */
182 #define CI_PREFIX_ELISION       26      /* Prefix elision */
183 #define CI_MULTILINK_HDR_FMT    27      /* Multilink header format */
184 #define CI_INTERNATIONALIZATION 28      /* Internationalization (RFC 2484) */
185 #define CI_SDL_ON_SONET_SDH     29      /* Simple Data Link on SONET/SDH */
186
187 static void dissect_lcp_mru_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
188                         int offset, guint length, proto_tree *tree);
189 static void dissect_lcp_async_map_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
190                         int offset, guint length, proto_tree *tree);
191 static void dissect_lcp_protocol_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
192                         int offset, guint length, proto_tree *tree);
193 static void dissect_lcp_magicnumber_opt(const ip_tcp_opt *optp,
194                         tvbuff_t *tvb, int offset, guint length,
195                         proto_tree *tree);
196 static void dissect_lcp_fcs_alternatives_opt(const ip_tcp_opt *optp,
197                         tvbuff_t *tvb, int offset, guint length,
198                         proto_tree *tree);
199 static void dissect_lcp_numbered_mode_opt(const ip_tcp_opt *optp,
200                         tvbuff_t *tvb, int offset, guint length,
201                         proto_tree *tree);
202 static void dissect_lcp_self_describing_pad_opt(const ip_tcp_opt *optp,
203                         tvbuff_t *tvb, int offset, guint length,
204                         proto_tree *tree);
205 static void dissect_lcp_callback_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
206                         int offset, guint length, proto_tree *tree);
207 static void dissect_lcp_multilink_mrru_opt(const ip_tcp_opt *optp,
208                         tvbuff_t *tvb, int offset, guint length,
209                         proto_tree *tree);
210 static void dissect_lcp_multilink_ep_disc_opt(const ip_tcp_opt *optp,
211                         tvbuff_t *tvb, int offset, guint length,
212                         proto_tree *tree);
213 static void dissect_lcp_bap_link_discriminator_opt(const ip_tcp_opt *optp,
214                         tvbuff_t *tvb, int offset, guint length,
215                         proto_tree *tree);
216 static void dissect_lcp_internationalization_opt(const ip_tcp_opt *optp,
217                         tvbuff_t *tvb, int offset, guint length,
218                         proto_tree *tree);
219 static void dissect_mp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
220
221 static const ip_tcp_opt lcp_opts[] = {
222         {
223                 CI_MRU,
224                 "Maximum Receive Unit",
225                 &ett_lcp_mru_opt,
226                 FIXED_LENGTH,
227                 4,
228                 dissect_lcp_mru_opt
229         },
230         {
231                 CI_ASYNCMAP,
232                 "Async Control Character Map",
233                 &ett_lcp_async_map_opt,
234                 FIXED_LENGTH,
235                 6,
236                 dissect_lcp_async_map_opt
237         },
238         {
239                 CI_AUTHTYPE,
240                 "Authentication protocol",
241                 &ett_lcp_authprot_opt,
242                 VARIABLE_LENGTH,
243                 4,
244                 dissect_lcp_protocol_opt
245         },
246         {
247                 CI_QUALITY,
248                 "Quality protocol",
249                 &ett_lcp_qualprot_opt,
250                 VARIABLE_LENGTH,
251                 4,
252                 dissect_lcp_protocol_opt
253         },
254         {
255                 CI_MAGICNUMBER,
256                 NULL,
257                 &ett_lcp_magicnum_opt,
258                 FIXED_LENGTH,
259                 6,
260                 dissect_lcp_magicnumber_opt
261         },
262         {
263                 CI_PCOMPRESSION,
264                 "Protocol field compression",
265                 NULL,
266                 FIXED_LENGTH,
267                 2,
268                 NULL
269         },
270         {
271                 CI_ACCOMPRESSION,
272                 "Address/control field compression",
273                 NULL,
274                 FIXED_LENGTH,
275                 2,
276                 NULL
277         },
278         {
279                 CI_FCS_ALTERNATIVES,
280                 NULL,
281                 &ett_lcp_fcs_alternatives_opt,
282                 FIXED_LENGTH,
283                 3,
284                 dissect_lcp_fcs_alternatives_opt
285         },
286         {
287                 CI_SELF_DESCRIBING_PAD,
288                 NULL,
289                 NULL,
290                 FIXED_LENGTH,
291                 3,
292                 dissect_lcp_self_describing_pad_opt
293         },
294         {
295                 CI_NUMBERED_MODE,
296                 "Numbered mode",
297                 &ett_lcp_numbered_mode_opt,
298                 VARIABLE_LENGTH,
299                 4,
300                 dissect_lcp_numbered_mode_opt
301         },
302         {
303                 CI_CALLBACK,
304                 "Callback",
305                 &ett_lcp_callback_opt,
306                 VARIABLE_LENGTH,
307                 3,
308                 dissect_lcp_callback_opt,
309         },
310         {
311                 CI_COMPOUND_FRAMES,
312                 "Compound frames",
313                 NULL,
314                 FIXED_LENGTH,
315                 2,
316                 NULL
317         },
318         {
319                 CI_MULTILINK_MRRU,
320                 NULL,
321                 NULL,
322                 FIXED_LENGTH,
323                 4,
324                 dissect_lcp_multilink_mrru_opt
325         },
326         {
327                 CI_MULTILINK_SSNH,
328                 "Use short sequence number headers",
329                 NULL,
330                 FIXED_LENGTH,
331                 2,
332                 NULL
333         },
334         {
335                 CI_MULTILINK_EP_DISC,
336                 "Multilink endpoint discriminator",
337                 &ett_lcp_multilink_ep_disc_opt,
338                 VARIABLE_LENGTH,
339                 3,
340                 dissect_lcp_multilink_ep_disc_opt,
341         },
342         {
343                 CI_DCE_IDENTIFIER,
344                 "DCE identifier",
345                 NULL,
346                 VARIABLE_LENGTH,
347                 2,
348                 NULL
349         },
350         {
351                 CI_MULTILINK_PLUS_PROC,
352                 "Multilink Plus Procedure",
353                 NULL,
354                 VARIABLE_LENGTH,
355                 2,
356                 NULL
357         },
358         {
359                 CI_LINK_DISC_FOR_BACP,
360                 NULL,
361                 NULL,
362                 FIXED_LENGTH,
363                 4,
364                 dissect_lcp_bap_link_discriminator_opt
365         },
366         {
367                 CI_LCP_AUTHENTICATION,
368                 "LCP authentication",
369                 NULL,
370                 VARIABLE_LENGTH,
371                 2,
372                 NULL
373         },
374         {
375                 CI_COBS,
376                 "Consistent Overhead Byte Stuffing",
377                 NULL,
378                 VARIABLE_LENGTH,
379                 2,
380                 NULL
381         },
382         {
383                 CI_PREFIX_ELISION,
384                 "Prefix elision",
385                 NULL,
386                 VARIABLE_LENGTH,
387                 2,
388                 NULL
389         },
390         {
391                 CI_MULTILINK_HDR_FMT,
392                 "Multilink header format",
393                 NULL,
394                 VARIABLE_LENGTH,
395                 2,
396                 NULL
397         },
398         {
399                 CI_INTERNATIONALIZATION,
400                 "Internationalization",
401                 &ett_lcp_internationalization_opt,
402                 VARIABLE_LENGTH,
403                 7,
404                 dissect_lcp_internationalization_opt
405         },
406         {
407                 CI_SDL_ON_SONET_SDH,
408                 "Simple data link on SONET/SDH",
409                 NULL,
410                 VARIABLE_LENGTH,
411                 2,
412                 NULL
413         }
414 };
415
416 #define N_LCP_OPTS      (sizeof lcp_opts / sizeof lcp_opts[0])
417
418 /*
419  * Options.  (IPCP)
420  */
421 #define CI_ADDRS        1       /* IP Addresses (deprecated) (RFC 1172) */
422 #define CI_COMPRESSTYPE 2       /* Compression Type (RFC 1332) */
423 #define CI_ADDR         3       /* IP Address (RFC 1332) */
424 #define CI_MOBILE_IPv4  4       /* Mobile IPv4 (RFC 2290) */
425 #define CI_MS_DNS1      129     /* Primary DNS value (RFC 1877) */
426 #define CI_MS_WINS1     130     /* Primary WINS value (RFC 1877) */
427 #define CI_MS_DNS2      131     /* Secondary DNS value (RFC 1877) */
428 #define CI_MS_WINS2     132     /* Secondary WINS value (RFC 1877) */
429
430 static void dissect_ipcp_addrs_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
431                         int offset, guint length, proto_tree *tree);
432 static void dissect_ipcp_addr_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
433                         int offset, guint length, proto_tree *tree);
434
435 static const ip_tcp_opt ipcp_opts[] = {
436         {
437                 CI_ADDRS,
438                 "IP addresses (deprecated)",
439                 &ett_ipcp_ipaddrs_opt,
440                 FIXED_LENGTH,
441                 10,
442                 dissect_ipcp_addrs_opt
443         },
444         {
445                 CI_COMPRESSTYPE,
446                 "IP compression protocol",
447                 &ett_ipcp_compressprot_opt,
448                 VARIABLE_LENGTH,
449                 4,
450                 dissect_lcp_protocol_opt
451         },
452         {
453                 CI_ADDR,
454                 "IP address",
455                 NULL,
456                 FIXED_LENGTH,
457                 6,
458                 dissect_ipcp_addr_opt
459         },
460         {
461                 CI_MOBILE_IPv4,
462                 "Mobile node's home IP address",
463                 NULL,
464                 FIXED_LENGTH,
465                 6,
466                 dissect_ipcp_addr_opt
467         },
468         {
469                 CI_MS_DNS1,
470                 "Primary DNS server IP address",
471                 NULL,
472                 FIXED_LENGTH,
473                 6,
474                 dissect_ipcp_addr_opt
475         },
476         {
477                 CI_MS_WINS1,
478                 "Primary WINS server IP address",
479                 NULL,
480                 FIXED_LENGTH,
481                 6,
482                 dissect_ipcp_addr_opt
483         },
484         {
485                 CI_MS_DNS2,
486                 "Secondary DNS server IP address",
487                 NULL,
488                 FIXED_LENGTH,
489                 6,
490                 dissect_ipcp_addr_opt
491         },
492         {
493                 CI_MS_WINS2,
494                 "Secondary WINS server IP address",
495                 NULL,
496                 FIXED_LENGTH,
497                 6,
498                 dissect_ipcp_addr_opt
499         }
500 };
501
502 #define N_IPCP_OPTS     (sizeof ipcp_opts / sizeof ipcp_opts[0])
503
504 void
505 capture_ppp( const u_char *pd, int offset, packet_counts *ld ) {
506   switch (pntohs(&pd[offset + 2])) {
507     case PPP_IP:
508       capture_ip(pd, offset + 4, ld);
509       break;
510     case PPP_IPX:
511       capture_ipx(pd, offset + 4, ld);
512       break;
513     case PPP_VINES:
514       capture_vines(pd, offset + 4, ld);
515       break;
516     default:
517       ld->other++;
518       break;
519   }
520 }
521
522 static void
523 dissect_lcp_mru_opt(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
524                         guint length, proto_tree *tree)
525 {
526   proto_tree_add_text(tree, tvb, offset, length, "MRU: %u",
527                         tvb_get_ntohs(tvb, offset + 2));
528 }
529
530 static void
531 dissect_lcp_async_map_opt(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
532                         guint length, proto_tree *tree)
533 {
534   proto_tree_add_text(tree, tvb, offset, length, "Async characters to map: 0x%08x",
535                         tvb_get_ntohl(tvb, offset + 2));
536 }
537
538 static void
539 dissect_lcp_protocol_opt(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
540                         guint length, proto_tree *tree)
541 {
542   guint16 protocol;
543   proto_item *tf;
544   proto_tree *field_tree = NULL;
545   
546   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
547           optp->name, length, plurality(length, "", "s"));
548   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
549   offset += 2;
550   length -= 2;
551   protocol = tvb_get_ntohs(tvb, offset);
552   proto_tree_add_text(field_tree, tvb, offset, 2, "%s: %s (0x%02x)", optp->name,
553                 val_to_str(protocol, ppp_vals, "Unknown"), protocol);
554   offset += 2;
555   length -= 2;
556   if (length > 0)
557     proto_tree_add_text(field_tree, tvb, offset, length, "Data (%d byte%s)", length,
558                         plurality(length, "", "s"));
559 }
560
561 static void
562 dissect_lcp_magicnumber_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
563                         int offset, guint length, proto_tree *tree)
564 {
565   proto_tree_add_text(tree, tvb, offset, length, "Magic number: 0x%08x",
566                         tvb_get_ntohl(tvb, offset + 2));
567 }
568
569 static void
570 dissect_lcp_fcs_alternatives_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
571                         int offset, guint length, proto_tree *tree)
572 {
573   proto_item *tf;
574   proto_tree *field_tree = NULL;
575   guint8 alternatives;
576   
577   alternatives = tvb_get_guint8(tvb, offset + 2);
578   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: 0x%02x",
579           optp->name, alternatives);
580   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
581   offset += 2;
582   if (alternatives & 0x1)
583     proto_tree_add_text(field_tree, tvb, offset + 2, 1, "%s",
584        decode_boolean_bitfield(alternatives, 0x1, 8, "Null FCS", NULL));
585   if (alternatives & 0x2)
586     proto_tree_add_text(field_tree, tvb, offset + 2, 1, "%s",
587        decode_boolean_bitfield(alternatives, 0x2, 8, "CCITT 16-bit FCS", NULL));
588   if (alternatives & 0x4)
589     proto_tree_add_text(field_tree, tvb, offset + 2, 1, "%s",
590        decode_boolean_bitfield(alternatives, 0x4, 8, "CCITT 32-bit FCS", NULL));
591 }
592
593 static void
594 dissect_lcp_self_describing_pad_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
595                         int offset, guint length, proto_tree *tree)
596 {
597   proto_tree_add_text(tree, tvb, offset, length,
598                         "Maximum octets of self-describing padding: %u",
599                         tvb_get_guint8(tvb, offset + 2));
600 }
601
602 static void
603 dissect_lcp_numbered_mode_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
604                         int offset, guint length, proto_tree *tree)
605 {
606   proto_item *tf;
607   proto_tree *field_tree = NULL;
608   
609   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
610           optp->name, length, plurality(length, "", "s"));
611   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
612   offset += 2;
613   length -= 2;
614   proto_tree_add_text(field_tree, tvb, offset, 1, "Window: %u",
615                         tvb_get_guint8(tvb, offset));
616   offset += 1;
617   length -= 1;
618   if (length > 0)
619     proto_tree_add_text(field_tree, tvb, offset, length, "Address (%d byte%s)",
620                         length, plurality(length, "", "s"));
621 }
622
623 static const value_string callback_op_vals[] = {
624         {0, "Location is determined by user authentication" },
625         {1, "Message is dialing string" },
626         {2, "Message is location identifier" },
627         {3, "Message is E.164" },
628         {4, "Message is distinguished name" },
629         {0, NULL }
630 };
631
632 static void
633 dissect_lcp_callback_opt(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
634                         guint length, proto_tree *tree)
635 {
636   proto_item *tf;
637   proto_tree *field_tree = NULL;
638   guint8 operation;
639   
640   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
641           optp->name, length, plurality(length, "", "s"));
642   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
643   offset += 2;
644   length -= 2;
645   operation = tvb_get_guint8(tvb, offset);
646   proto_tree_add_text(field_tree, tvb, offset, 1, "Operation: %s (0x%02x)",
647                 val_to_str(operation, callback_op_vals, "Unknown"),
648                 operation);
649   offset += 1;
650   length -= 1;
651   if (length > 0)
652     proto_tree_add_text(field_tree, tvb, offset, length, "Message (%d byte%s)",
653                         length, plurality(length, "", "s"));
654 }
655
656 static void
657 dissect_lcp_multilink_mrru_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
658                         int offset, guint length, proto_tree *tree)
659 {
660   proto_tree_add_text(tree, tvb, offset, length, "Multilink MRRU: %u",
661                         tvb_get_ntohs(tvb, offset + 2));
662 }
663
664 #define CLASS_NULL                      0
665 #define CLASS_LOCAL                     1
666 #define CLASS_IP                        2
667 #define CLASS_IEEE_802_1                3
668 #define CLASS_PPP_MAGIC_NUMBER          4
669 #define CLASS_PSDN_DIRECTORY_NUMBER     5
670
671 static const value_string multilink_ep_disc_class_vals[] = {
672         {CLASS_NULL,                  "Null" },
673         {CLASS_LOCAL,                 "Locally assigned address" },
674         {CLASS_IP,                    "IP address" },
675         {CLASS_IEEE_802_1,            "IEEE 802.1 globally assigned MAC address" },
676         {CLASS_PPP_MAGIC_NUMBER,      "PPP magic-number block" },
677         {CLASS_PSDN_DIRECTORY_NUMBER, "Public switched network directory number" },
678         {0,                           NULL }
679 };
680
681 static void
682 dissect_lcp_multilink_ep_disc_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
683                         int offset, guint length, proto_tree *tree)
684 {
685   proto_item *tf;
686   proto_tree *field_tree = NULL;
687   guint8 ep_disc_class;
688
689   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
690           optp->name, length, plurality(length, "", "s"));
691   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
692   offset += 2;
693   length -= 2;
694   ep_disc_class = tvb_get_guint8(tvb, offset);
695   proto_tree_add_text(field_tree, tvb, offset, 1, "Class: %s (%u)",
696                 val_to_str(ep_disc_class, multilink_ep_disc_class_vals, "Unknown"),
697                 ep_disc_class);
698   offset += 1;
699   length -= 1;
700   if (length > 0) {
701     switch (ep_disc_class) {
702
703     case CLASS_NULL:
704       proto_tree_add_text(field_tree, tvb, offset, length,
705                         "Address (%d byte%s), should have been empty",
706                         length, plurality(length, "", "s"));
707       break;
708
709     case CLASS_LOCAL:
710       if (length > 20) {
711         proto_tree_add_text(field_tree, tvb, offset, length,
712                         "Address (%d byte%s), should have been <20",
713                         length, plurality(length, "", "s"));
714       } else {
715         proto_tree_add_text(field_tree, tvb, offset, length,
716                         "Address (%d byte%s)",
717                         length, plurality(length, "", "s"));
718       }
719       break;
720
721     case CLASS_IP:
722       if (length != 4) {
723         proto_tree_add_text(field_tree, tvb, offset, length,
724                         "Address (%d byte%s), should have been 4",
725                         length, plurality(length, "", "s"));
726       } else {
727         proto_tree_add_text(field_tree, tvb, offset, length,
728                         "Address: %s", ip_to_str(tvb_get_ptr(tvb, offset, 4)));
729       }
730       break;
731
732     case CLASS_IEEE_802_1:
733       if (length != 6) {
734         proto_tree_add_text(field_tree, tvb, offset, length,
735                         "Address (%d byte%s), should have been 6",
736                         length, plurality(length, "", "s"));
737       } else {
738         proto_tree_add_text(field_tree, tvb, offset, length,
739                         "Address: %s", ether_to_str(tvb_get_ptr(tvb, offset, 6)));
740       }
741       break;
742
743     case CLASS_PPP_MAGIC_NUMBER:
744       /* XXX - dissect as 32-bit magic numbers */
745       if (length > 20) {
746         proto_tree_add_text(field_tree, tvb, offset, length,
747                         "Address (%d byte%s), should have been <20",
748                         length, plurality(length, "", "s"));
749       } else {
750         proto_tree_add_text(field_tree, tvb, offset, length,
751                         "Address (%d byte%s)",
752                         length, plurality(length, "", "s"));
753       }
754       break;
755
756     case CLASS_PSDN_DIRECTORY_NUMBER:
757       if (length > 15) {
758         proto_tree_add_text(field_tree, tvb, offset, length,
759                         "Address (%d byte%s), should have been <20",
760                         length, plurality(length, "", "s"));
761       } else {
762         proto_tree_add_text(field_tree, tvb, offset, length,
763                         "Address (%d byte%s)",
764                         length, plurality(length, "", "s"));
765       }
766       break;
767
768     default:
769       proto_tree_add_text(field_tree, tvb, offset, length,
770                         "Address (%d byte%s)",
771                         length, plurality(length, "", "s"));
772       break;
773     }
774   }
775 }
776
777 static void
778 dissect_lcp_bap_link_discriminator_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
779                         int offset, guint length, proto_tree *tree)
780 {
781   proto_tree_add_text(tree, tvb, offset, length,
782                         "Link discriminator for BAP: 0x%04x",
783                         tvb_get_ntohs(tvb, offset + 2));
784 }
785
786 /* Character set numbers from the IANA charset registry. */
787 static const value_string charset_num_vals[] = {
788         {105, "UTF-8" },
789         {0,   NULL }
790 };
791
792 static void
793 dissect_lcp_internationalization_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
794                         int offset, guint length, proto_tree *tree)
795 {
796   proto_item *tf;
797   proto_tree *field_tree = NULL;
798   guint32 charset;
799   
800   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
801           optp->name, length, plurality(length, "", "s"));
802   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
803   offset += 2;
804   length -= 2;
805   charset = tvb_get_ntohl(tvb, offset);
806   proto_tree_add_text(field_tree, tvb, offset, 4, "Character set: %s (0x%04x)",
807                 val_to_str(charset, charset_num_vals, "Unknown"),
808                 charset);
809   offset += 4;
810   length -= 4;
811   if (length > 0) {
812     /* XXX - should be displayed as an ASCII string */
813     proto_tree_add_text(field_tree, tvb, offset, length, "Language tag (%d byte%s)",
814                         length, plurality(length, "", "s"));
815   }
816 }
817
818 static void
819 dissect_ipcp_addrs_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
820                         int offset, guint length, proto_tree *tree)
821 {
822   proto_item *tf;
823   proto_tree *field_tree = NULL;
824   
825   tf = proto_tree_add_text(tree, tvb, offset, length, "%s: %u byte%s",
826           optp->name, length, plurality(length, "", "s"));
827   field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
828   offset += 2;
829   length -= 2;
830   proto_tree_add_text(field_tree, tvb, offset, 4,
831                         "Source IP address: %s",
832                         ip_to_str(tvb_get_ptr(tvb, offset, 4)));
833   offset += 4;
834   length -= 4;
835   proto_tree_add_text(field_tree, tvb, offset, 4,
836                         "Destination IP address: %s",
837                         ip_to_str(tvb_get_ptr(tvb, offset, 4)));
838 }
839
840 static void dissect_ipcp_addr_opt(const ip_tcp_opt *optp, tvbuff_t *tvb,
841                         int offset, guint length, proto_tree *tree)
842 {
843   proto_tree_add_text(tree, tvb, offset, length, "%s: %s", optp->name,
844                         ip_to_str(tvb_get_ptr(tvb, offset + 2, 4)));
845 }
846
847 static void
848 dissect_cp( tvbuff_t *tvb, const char *proto_short_name,
849         const char *proto_long_name, int proto_subtree_index,
850         const value_string *proto_vals, int options_subtree_index,
851         const ip_tcp_opt *opts, int nopts, packet_info *pinfo, proto_tree *tree ) {
852   proto_item *ti;
853   proto_tree *fh_tree = NULL;
854   proto_item *tf;
855   proto_tree *field_tree;
856
857   guint8 code;
858   guint8 id;
859   int length, offset;
860   guint16 protocol;
861
862   code = tvb_get_guint8(tvb, 0);
863   id = tvb_get_guint8(tvb, 1);
864   length = tvb_get_ntohs(tvb, 2);
865
866   if(check_col(pinfo->fd, COL_INFO))
867         col_add_fstr(pinfo->fd, COL_INFO, "%sCP %s", proto_short_name,
868                 val_to_str(code, proto_vals, "Unknown"));
869
870   if(tree) {
871     ti = proto_tree_add_text(tree, tvb, 0, 4, "%s Control Protocol",
872                                 proto_long_name);
873     fh_tree = proto_item_add_subtree(ti, proto_subtree_index);
874     proto_tree_add_text(fh_tree, tvb, 0, 1, "Code: %s (0x%02x)",
875       val_to_str(code, proto_vals, "Unknown"), code);
876     proto_tree_add_text(fh_tree, tvb, 1, 1, "Identifier: 0x%02x",
877                         id);
878     proto_tree_add_text(fh_tree, tvb, 2, 2, "Length: %u",
879                         length);
880   }
881   offset = 4;
882   length -= 4;
883
884   switch (code) {
885     case CONFREQ:
886     case CONFACK:
887     case CONFNAK:
888     case CONFREJ:
889       if(tree) {
890         if (length > 0) {
891           tf = proto_tree_add_text(fh_tree, tvb, offset, length,
892             "Options: (%d byte%s)", length, plurality(length, "", "s"));
893           field_tree = proto_item_add_subtree(tf, options_subtree_index);
894           dissect_ip_tcp_options(tvb, offset, length, opts, nopts, -1,
895                                  field_tree);
896         }
897       }
898       break;
899
900     case ECHOREQ:
901     case ECHOREP:
902     case DISCREQ:
903     case IDENT:
904       if(tree) {
905         proto_tree_add_text(fh_tree, tvb, offset, 4, "Magic number: 0x%08x",
906                         tvb_get_ntohl(tvb, offset));
907         offset += 4;
908         length -= 4;
909         if (length > 0)
910           proto_tree_add_text(fh_tree, tvb, offset, length, "Message (%d byte%s)",
911                                 length, plurality(length, "", "s"));
912       }
913       break;
914
915     case TIMEREMAIN:
916       if(tree) {
917         proto_tree_add_text(fh_tree, tvb, offset, 4, "Magic number: 0x%08x",
918                         tvb_get_ntohl(tvb, offset));
919         offset += 4;
920         length -= 4;
921         proto_tree_add_text(fh_tree, tvb, offset, 4, "Seconds remaining: %u",
922                         tvb_get_ntohl(tvb, offset));
923         offset += 4;
924         length -= 4;
925         if (length > 0)
926           proto_tree_add_text(fh_tree, tvb, offset, length, "Message (%d byte%s)",
927                                 length, plurality(length, "", "s"));
928       }
929       break;
930
931     case PROTREJ:
932       if(tree) {
933         protocol = tvb_get_ntohs(tvb, offset);
934         proto_tree_add_text(fh_tree, tvb, offset, 2, "Rejected protocol: %s (0x%04x)",
935                 val_to_str(protocol, ppp_vals, "Unknown"), protocol);
936         offset += 2;
937         length -= 2;
938         if (length > 0)
939           proto_tree_add_text(fh_tree, tvb, offset, length, "Rejected packet (%d byte%s)",
940                                 length, plurality(length, "", "s"));
941                 /* XXX - should be dissected as a PPP packet */
942       }
943       break;
944
945     case CODEREJ:
946                 /* decode the rejected LCP packet here. */
947       if (length > 0)
948         proto_tree_add_text(fh_tree, tvb, offset, length, "Rejected packet (%d byte%s)",
949                                 length, plurality(length, "", "s"));
950       break;
951
952     case TERMREQ:
953     case TERMACK:
954       if (length > 0)
955         proto_tree_add_text(fh_tree, tvb, offset, length, "Data (%d byte%s)",
956                                 length, plurality(length, "", "s"));
957       break;
958
959     default:
960       if (length > 0)
961         proto_tree_add_text(fh_tree, tvb, offset, length, "Stuff (%d byte%s)",
962                                 length, plurality(length, "", "s"));
963       break;
964   }
965 }
966
967 /* Protocol field compression */
968 #define PFC_BIT 0x01
969
970 static gboolean
971 dissect_ppp_stuff( tvbuff_t *tvb, packet_info *pinfo,
972                 proto_tree *tree, proto_tree *fh_tree ) {
973   guint16 ppp_prot;
974   int     proto_len;
975   tvbuff_t      *next_tvb;
976
977   if (tvb_get_guint8(tvb, 0) & PFC_BIT) {
978     ppp_prot = tvb_get_guint8(tvb, 0);
979     proto_len = 1;
980   } else {
981     ppp_prot = tvb_get_ntohs(tvb, 0);
982     proto_len = 2;
983   }
984
985   if (tree) {
986     proto_tree_add_text(fh_tree, tvb, 0, proto_len, "Protocol: %s (0x%04x)",
987       val_to_str(ppp_prot, ppp_vals, "Unknown"), ppp_prot);
988   }
989
990   next_tvb = tvb_new_subset(tvb, proto_len, -1, -1);
991
992   /* do lookup with the subdissector table */
993   if (dissector_try_port(subdissector_table, ppp_prot, next_tvb, pinfo, tree))
994       return TRUE;
995
996   /* XXX - make "dissect_lcp()" and "dissect_ipcp()", have them just
997      call "dissect_cp()", and register them as well?
998
999      We can do that for "dissect_mp()", too. */
1000   switch (ppp_prot) {
1001     case PPP_MP:
1002       dissect_mp(next_tvb, pinfo, tree);
1003       return TRUE;
1004     case PPP_LCP:
1005       dissect_cp(next_tvb, "L", "Link", ett_lcp, lcp_vals, ett_lcp_options,
1006                 lcp_opts, N_LCP_OPTS, pinfo, tree);
1007       return TRUE;
1008     case PPP_IPCP:
1009       dissect_cp(next_tvb, "IP", "IP", ett_ipcp, cp_vals, ett_ipcp_options,
1010                 ipcp_opts, N_IPCP_OPTS, pinfo, tree);
1011       return TRUE;
1012     default:
1013       if (check_col(pinfo->fd, COL_INFO))
1014         col_add_fstr(pinfo->fd, COL_INFO, "PPP %s (0x%04x)",
1015                 val_to_str(ppp_prot, ppp_vals, "Unknown"), ppp_prot);
1016       dissect_data(next_tvb, 0, pinfo, tree);
1017       return FALSE;
1018   }
1019 }
1020
1021 #define MP_FRAG_MASK     0xC0
1022 #define MP_FRAG(bits)    ((bits) & MP_FRAG_MASK)
1023 #define MP_FRAG_FIRST    0x80
1024 #define MP_FRAG_LAST     0x40
1025 #define MP_FRAG_RESERVED 0x3f
1026
1027 /* According to RFC 1717, the length the MP header isn't indicated anywhere
1028    in the header itself.  It starts out at four bytes and can be
1029    negotiated down to two using LCP.  We currently assume that all
1030    headers are four bytes.  - gcc
1031  */
1032 static void
1033 dissect_mp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1034 {
1035   proto_tree *mp_tree, *hdr_tree, *fh_tree = NULL;
1036   proto_item *ti;
1037   guint8      flags;
1038   guint32     seq;
1039   gchar       *flag_str;
1040   int         first, last;
1041   tvbuff_t      *next_tvb;
1042
1043   CHECK_DISPLAY_AS_DATA(proto_mp, tvb, pinfo, tree);
1044
1045   flags = tvb_get_guint8(tvb, 0);
1046   first = flags && MP_FRAG_FIRST;
1047   last  = flags && MP_FRAG_LAST;
1048   seq   = tvb_get_ntoh24(tvb, 1);
1049
1050   if (check_col(pinfo->fd, COL_INFO))
1051     col_add_fstr(pinfo->fd, COL_INFO, "PPP Multilink");
1052
1053   if (tree) {
1054     switch (flags) {
1055       case MP_FRAG_FIRST:
1056         flag_str = "First";
1057         break;
1058       case MP_FRAG_LAST:
1059         flag_str = "Last";
1060         break;
1061       case MP_FRAG_FIRST|MP_FRAG_LAST:
1062         flag_str = "First, Last";
1063         break;
1064       default:
1065         flag_str = "Unknown";
1066         break;
1067     }
1068     ti = proto_tree_add_item(tree, proto_mp, tvb, 0, 4, FALSE);
1069     mp_tree = proto_item_add_subtree(ti, ett_mp);
1070     ti = proto_tree_add_text(mp_tree, tvb, 0, 1, "Fragment: 0x%2X (%s)",
1071       flags, flag_str);
1072     hdr_tree = proto_item_add_subtree(ti, ett_mp_flags);
1073     proto_tree_add_boolean_format(hdr_tree, hf_mp_frag_first, tvb, 0, 1, first,
1074       "%s", decode_boolean_bitfield(flags, MP_FRAG_FIRST, sizeof(flags) * 8,
1075         "first", "not first"));
1076     proto_tree_add_boolean_format(hdr_tree, hf_mp_frag_last, tvb, 0, 1, last,
1077       "%s", decode_boolean_bitfield(flags, MP_FRAG_LAST, sizeof(flags) * 8,
1078         "last", "not last"));
1079     proto_tree_add_text(hdr_tree, tvb, 0, 1, "%s",
1080       decode_boolean_bitfield(flags, MP_FRAG_RESERVED, sizeof(flags) * 8,
1081         "reserved", "reserved"));
1082     proto_tree_add_uint(mp_tree, hf_mp_sequence_num, tvb,  1, 3, seq);
1083  }
1084
1085   next_tvb = tvb_new_subset(tvb, 4, -1, -1);
1086
1087   if (tvb_length(next_tvb) > 0) {
1088           if (tree) {
1089             ti = proto_tree_add_item(tree, proto_ppp, next_tvb, 0, 1, FALSE);
1090             fh_tree = proto_item_add_subtree(ti, ett_ppp);
1091           }
1092           dissect_ppp_stuff(next_tvb, pinfo, tree, fh_tree);
1093   }
1094 }
1095
1096 static void
1097 dissect_payload_ppp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) {
1098   proto_item *ti;
1099   proto_tree *fh_tree = NULL;
1100   tvbuff_t   *next_tvb;
1101
1102   /* XXX - the length shouldn't be 2, it should be based on the length
1103      of the protocol field. */
1104   if(tree) {
1105     ti = proto_tree_add_item(tree, proto_ppp, tvb, 0, 2, FALSE);
1106     fh_tree = proto_item_add_subtree(ti, ett_ppp);
1107   }
1108
1109   dissect_ppp_stuff(tvb, pinfo, tree, fh_tree);
1110 }
1111
1112 void
1113 dissect_ppp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) {
1114   e_ppphdr   ph;
1115   proto_item *ti;
1116   proto_tree *fh_tree = NULL;
1117   int        proto_offset;
1118   tvbuff_t   *next_tvb;
1119   guint8     byte0;
1120
1121   CHECK_DISPLAY_AS_DATA(proto_ppp, tvb, pinfo, tree);
1122
1123   pinfo->current_proto = "PPP";
1124   byte0 = tvb_get_guint8(tvb, 0);
1125
1126   if (byte0 == 0xff) {
1127     ph.ppp_addr = tvb_get_guint8(tvb, 0);
1128     ph.ppp_ctl  = tvb_get_guint8(tvb, 1);
1129     ph.ppp_prot = tvb_get_ntohs(tvb, 2);
1130     proto_offset =  2;
1131   }
1132   else {
1133     /* address and control are compressed (NULL) */
1134     ph.ppp_prot = tvb_get_ntohs(tvb, 0);
1135     proto_offset = 0;
1136   }
1137
1138   /* load the top pane info. This should be overwritten by
1139      the next protocol in the stack */
1140
1141   if(check_col(pinfo->fd, COL_RES_DL_SRC))
1142     col_add_str(pinfo->fd, COL_RES_DL_SRC, "N/A" );
1143   if(check_col(pinfo->fd, COL_RES_DL_DST))
1144     col_add_str(pinfo->fd, COL_RES_DL_DST, "N/A" );
1145   if(check_col(pinfo->fd, COL_PROTOCOL))
1146     col_add_str(pinfo->fd, COL_PROTOCOL, "PPP" );
1147
1148   if(tree) {
1149     ti = proto_tree_add_item(tree, proto_ppp, tvb, 0, 4, FALSE);
1150     fh_tree = proto_item_add_subtree(ti, ett_ppp);
1151     if (byte0 == 0xff) {
1152       proto_tree_add_text(fh_tree, tvb, 0, 1, "Address: %02x", ph.ppp_addr);
1153       proto_tree_add_text(fh_tree, tvb, 1, 1, "Control: %02x", ph.ppp_ctl);
1154     }
1155   }
1156
1157   next_tvb = tvb_new_subset(tvb, proto_offset, -1, -1);
1158
1159   if (!dissect_ppp_stuff(next_tvb, pinfo, tree, fh_tree)) {
1160     if (check_col(pinfo->fd, COL_PROTOCOL))
1161       col_add_fstr(pinfo->fd, COL_PROTOCOL, "0x%04x", ph.ppp_prot);
1162   }
1163 }
1164
1165 void
1166 proto_register_ppp(void)
1167 {
1168 /*        static hf_register_info hf[] = {
1169                 { &variable,
1170                 { "Name",           "ppp.abbreviation", TYPE, VALS_POINTER }},
1171         };*/
1172         static gint *ett[] = {
1173                 &ett_ppp,
1174                 &ett_ipcp,
1175                 &ett_ipcp_options,
1176                 &ett_ipcp_ipaddrs_opt,
1177                 &ett_ipcp_compressprot_opt,
1178                 &ett_lcp,
1179                 &ett_lcp_options,
1180                 &ett_lcp_mru_opt,
1181                 &ett_lcp_async_map_opt,
1182                 &ett_lcp_authprot_opt,
1183                 &ett_lcp_qualprot_opt,
1184                 &ett_lcp_magicnum_opt,
1185                 &ett_lcp_fcs_alternatives_opt,
1186                 &ett_lcp_numbered_mode_opt,
1187                 &ett_lcp_callback_opt,
1188                 &ett_lcp_multilink_ep_disc_opt,
1189                 &ett_lcp_internationalization_opt,
1190         };
1191
1192         proto_ppp = proto_register_protocol("Point-to-Point Protocol", "ppp");
1193  /*       proto_register_field_array(proto_ppp, hf, array_length(hf));*/
1194         proto_register_subtree_array(ett, array_length(ett));
1195
1196 /* subdissector code */
1197         subdissector_table = register_dissector_table("ppp.protocol");
1198
1199         register_dissector("ppp", dissect_ppp);
1200         register_dissector("payload_ppp", dissect_payload_ppp);
1201 }
1202
1203 void
1204 proto_register_mp(void)
1205 {
1206   static hf_register_info hf[] = {
1207     { &hf_mp_frag_first,
1208     { "First fragment",         "mp.first",     FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1209         "" }},
1210
1211     { &hf_mp_frag_last,
1212     { "Last fragment",          "mp.last",      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1213         "" }},
1214
1215     { &hf_mp_sequence_num,
1216     { "Sequence number",        "mp.seq",       FT_UINT24, BASE_DEC, NULL, 0x0,
1217         "" }}
1218   };
1219   static gint *ett[] = {
1220     &ett_mp,
1221     &ett_mp_flags,
1222   };
1223
1224   proto_mp = proto_register_protocol("PPP Multilink Protocol", "mp");
1225   proto_register_field_array(proto_mp, hf, array_length(hf));
1226   proto_register_subtree_array(ett, array_length(ett));
1227 }