Ethereal-dev: Re: [ethereal-dev] LAPB/X.25 patch

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Guy Harris <gharris@xxxxxxxxxxxx>
Date: Mon, 2 Aug 1999 23:26:40 -0700
> Hmmm, NetBEUI uses SABME, but looks remarkably like X.25 apart from that.

Or, at least, like LAPB; it uses LLC Type 1 and 2, and I think LLC, like
thousands of other protocols across the globe (LAPB, LAPD, etc.) is an
HDLC derivative (amazing how many packets flying around the world are
ultimately descended from something IBM came up with to let 3270
terminals and mainframes communicate...).

> I am not sure if it uses a modulo-128 window size, but probably does.

I seem to remember seeing something on some Web page saying it does, but
maybe I'm thinking of

	http://www.lebarge.com/clark/NetBasics/netbeui.htm

which said both

	The client computer sends a Set Asynchronous Balance Mode
	Extended (SABME) frame, and the server returns an Unnumbered
	Acknowledgment (UA) frame.  Then the client sends a Receive
	Ready (RR) frame, notifying the server that it is ready to
	receive Informational (I) frames whose sequence number is
	currently 0.  The server acknowledges this frame.

and

	THERE WOULD BE A PICTURE HERE IF MICROSOFT WASN'T ON CRACK AND
	PUT AN IMAGE OF THE TDI AND TCP/IP CONNECTION.

> Anyway, seems like the two protocols share a lot ...

Yup.

Here's a patch to the current CVS tree, which adds "xdlc.c and "xdlc.h"
files; "xdlc.c" defines a routine "dissect_xdlc_control()" to dissect
the control field of a frame in an SDLC-derived protocol (although there
may be some places where the processing has to be protocol-dependent). 

I modified "packet-llc.c" and "packet-lapb.c" to use it; it creates a
subtree for the control field, giving a bitfield decode if you open up
the subtree.  It *should* handle both basic and extended mode - its
caller passes in an argument to specify which mode it is (although I
guess they should pass in a pointer to that flag, and it should set that
flag if it sees one of the Set XXX Mode Extended commands; there'd still
be a need to set it from outside, though, as you might not have that
command in the capture).

Olivier, feel free to change it; the only frames I deal with that have
SDLC-derived protocols in them are FDDI and 802.3 frames, and I'm not
sure I've seen any one other than a Boring Old Unnumbered Information
field.  I'm not checking it in, so that people can review it first (so
if you want changes, tell me what you want changed, or send in a patch
to do the change).

Note that I will probably want to change "decode_numeric_bitfield()" so
that it either

	1) takes as an argument the bitfield value shifted appropriately

or

	2) shifts it appropriately itself

so that the N(R) and N(S) values display properly; the other files that
use it are "packet-gre.c" and "packet-tr.c", and I *suspect* they'd want
it to display them properly (i.e., if a bitfield is the upper 5 bits of
a byte, and the byte has the value

	01100111

the value should be 12 or 0xC, not 96 or 0x60) - if that's not the case,
let me know.  (If it is the case, I can do the shifting inside
"decode_numeric_bitfield()"; if it's not the case, its caller will have
to do the shifting, or not do it, as the case may be, and pass the
result on to "decode_numeric_bitfield()".)

diff -c -N ../ethereal.vanilla/Makefile.am ./Makefile.am
*** ../ethereal.vanilla/Makefile.am	Sun Aug  1 19:04:25 1999
--- ./Makefile.am	Mon Aug  2 20:57:07 1999
***************
*** 104,114 ****
  	resolv.c       \
  	resolv.h       \
  	smb.h          \
- 	timestamp.h    \
- 	util.c         \
          summary.c      \
          summary.h      \
! 	util.h
  
  EXTRA_ethereal_SOURCES = \
  	dfilter-grammar.c \
--- 104,116 ----
  	resolv.c       \
  	resolv.h       \
  	smb.h          \
          summary.c      \
          summary.h      \
! 	timestamp.h    \
! 	util.c         \
! 	util.h         \
! 	xdlc.c         \
! 	xdlc.h
  
  EXTRA_ethereal_SOURCES = \
  	dfilter-grammar.c \
diff -c -N ../ethereal.vanilla/packet-lapb.c ./packet-lapb.c
*** ../ethereal.vanilla/packet-lapb.c	Sun Aug  1 19:26:17 1999
--- ./packet-lapb.c	Mon Aug  2 21:41:13 1999
***************
*** 34,53 ****
  #include <glib.h>
  #include <string.h>
  #include "packet.h"
! 
! #define LAPB_I          0x00    /* Information frames */
! #define LAPB_S          0x01    /* Supervisory frames */
! #define LAPB_U          0x03    /* Unnumbered frames */
! 
! #define LAPB_RR         0x01    /* Receiver ready */
! #define LAPB_RNR        0x05    /* Receiver not ready */
! #define LAPB_REJ        0x09    /* Reject */
! #define LAPB_SABM       0x2F    /* Set Asynchronous Balanced Mode */
! #define LAPB_SABME      0x6F    /* Set Asynchronous Balanced Mode Extended */
! #define LAPB_DISC       0x43    /* Disconnect */
! #define LAPB_DM         0x0F    /* Disconnected mode */
! #define LAPB_UA         0x63    /* Unnumbered acknowledge */
! #define LAPB_FRMR       0x87    /* Frame reject */
  
  #define FROM_DCE	0x80
  
--- 34,40 ----
  #include <glib.h>
  #include <string.h>
  #include "packet.h"
! #include "xdlc.h"
  
  #define FROM_DCE	0x80
  
***************
*** 59,438 ****
  dissect_lapb(const u_char *pd, frame_data *fd, proto_tree *tree)
  {
      proto_tree *lapb_tree, *ti;
!     char lapb_addr[3];
!     char info[80];
  
      if (check_col(fd, COL_PROTOCOL))
  	col_add_str(fd, COL_PROTOCOL, "LAPB");
  
-     sprintf(lapb_addr, "%2d", (int)pd[0]);
      if(check_col(fd, COL_RES_DL_SRC))
! 	col_add_str(fd, COL_RES_DL_SRC, lapb_addr);
! 
!     switch (pd[1] & 0x0F) {
!     case LAPB_RR:
! 	if(check_col(fd, COL_INFO)) {
! 	    sprintf(info, "RR N(R):%d", (pd[1] >> 5) & 0x7);
! 	    if ((pd[1] >> 4) && 0x01) { /* P/F bit */
! 		if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
! 		    (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
! 		    strcat(info, " F");
! 		else
! 		    strcat(info, " P");
! 	    }
! 	    col_add_str(fd, COL_INFO, info);
! 	}
! 	if (fd->flags & FROM_DCE) {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DTE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	}
! 	else {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DCE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	}
! 	if (tree)
! 	{
! 	    ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 					    "LAPB");
! 	    lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
! 				       "Address : 0x%02X", (int)pd[0]);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "RR",
! 				       "Control field : 0x%02X", (int)pd[1]);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     %d%d%d..... : N(R) = %d",
! 				(pd[1] >> 7) & 0x1,
! 				(pd[1] >> 6) & 0x1,
! 				(pd[1] >> 5) & 0x1,
! 				(pd[1] >> 5) & 0x7);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ...%d.... : Poll/Final bit",
! 				(pd[1] >> 4) & 0x1);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ....0001 : Receive Ready (RR)");
! 	}
! 	return;
!     case LAPB_RNR:
! 	if(check_col(fd, COL_INFO)) {
! 	    sprintf(info, "RNR N(R):%d", (pd[1] >> 5) & 0x7);
! 	    if ((pd[1] >> 4) && 0x01) { /* P/F bit */
! 		if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
! 		    (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
! 		    strcat(info, " F");
! 		else
! 		    strcat(info, " P");
! 	    }
! 	    col_add_str(fd, COL_INFO, info);
! 	}
! 	if (fd->flags & FROM_DCE) {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DTE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	}
! 	else {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DCE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	}
! 	if (tree)
! 	{
! 	    ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 					    "LAPB");
! 	    lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
! 				       "Address : 0x%02X", (int)pd[0]);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "RNR",
! 				       "Control field : 0x%02X", (int)pd[1]);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     %d%d%d..... : N(R) = %d",
! 				(pd[1] >> 7) & 0x1,
! 				(pd[1] >> 6) & 0x1,
! 				(pd[1] >> 5) & 0x1,
! 				(pd[1] >> 5) & 0x7);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ...%d.... : Poll/Final bit",
! 				(pd[1] >> 4) & 0x1);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ....0101 : Receive Not Ready (RNR)");
! 	}
! 	return;
!     case LAPB_REJ:
! 	if(check_col(fd, COL_INFO)) {
! 	    sprintf(info, "REJ N(R):%d", (pd[1] >> 5) & 0x7);
! 	    if ((pd[1] >> 4) && 0x01) { /* P/F bit */
! 		if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
! 		    (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
! 		    strcat(info, " F");
! 		else
! 		    strcat(info, " P");
! 	    }
! 	    col_add_str(fd, COL_INFO, info);
! 	}
! 	if (fd->flags & FROM_DCE) {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DTE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	}
! 	else {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DCE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	}
! 	if (tree)
! 	{
! 	    ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 					    "LAPB");
! 	    lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
! 				       "Address : 0x%02X", (int)pd[0]);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1, "REJ",
! 				       "Control field : 0x%02X", (int)pd[1]);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     %d%d%d..... : N(R) = %d",
! 				(pd[1] >> 7) & 0x1,
! 				(pd[1] >> 6) & 0x1,
! 				(pd[1] >> 5) & 0x1,
! 				(pd[1] >> 5) & 0x7);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ...%d.... : Poll/Final bit",
! 				(pd[1] >> 4) & 0x1);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ....1001 : Reeject (REJ)");
! 	}
! 	return;
      }
  
!     /* not a RR/RNR/REJ frame */
  
!     if (pd[1] & 0x01) { /* not an information frame */
! 	switch (pd[1] & 0xEF) { /* don't check Poll/Final bit */
! 	case LAPB_SABM:
! 	    if (fd->flags & FROM_DCE) {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	    }
! 	    else {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	    }
! 	    if(check_col(fd, COL_INFO)) {
! 		if (pd[1] & 0x10)
! 		    col_add_str(fd, COL_INFO, "SABM P");
! 		else
! 		    col_add_str(fd, COL_INFO, "SABM");
! 	    }
! 	    if (tree) {
! 		ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 						"LAPB");
! 		lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 					   pd[0], "Address: 0x%02X",
! 					   (int)pd[0]);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 					   "SABM",
! 					   "Set Asynchronous Balanced Mode (SABM)");
! 		proto_tree_add_text(lapb_tree, 1, 1,
! 				    "...%d.... : Poll bit",
! 				    (pd[1] >> 4) & 0x1);
! 	    }
! 	    break;
! 	case LAPB_DISC:
! 	    if (fd->flags & FROM_DCE) {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	    }
! 	    else {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	    }
! 	    if(check_col(fd, COL_INFO)) {
! 		if (pd[1] & 0x10)
! 		    col_add_str(fd, COL_INFO, "DISC P");
! 		else
! 		    col_add_str(fd, COL_INFO, "DISC");
! 	    }
! 	    if (tree) {
! 		ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 						"LAPB");
! 		lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 					   pd[0], "Address: 0x%02X",
! 					   (int)pd[0]);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 					   "DISC", "Disconnect (DISC)");
! 		proto_tree_add_text(lapb_tree, 1, 1,
! 				    "...%d.... : Poll bit",
! 				    (pd[1] >> 4) & 0x1);
! 	    }
! 	    break;
! 	case LAPB_DM:
! 	    if (fd->flags & FROM_DCE) {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	    }
! 	    else {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	    }
! 	    if(check_col(fd, COL_INFO)) {
! 		if (pd[1] & 0x10)
! 		    col_add_str(fd, COL_INFO, "DM F");
! 		else
! 		    col_add_str(fd, COL_INFO, "DM");
! 	    }
! 	    if (tree) {
! 		ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 						"LAPB");
! 		lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 					   pd[0], "Address: 0x%02X",
! 					   (int)pd[0]);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 					   "DM", "Disconnect Mode (DM)");
! 		proto_tree_add_text(lapb_tree, 1, 1,
! 				    "...%d.... : Final bit",
! 				    (pd[1] >> 4) & 0x1);
! 	    }
! 	    break;
! 	case LAPB_UA:
! 	    if (fd->flags & FROM_DCE) {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	    }
! 	    else {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	    }
! 	    if(check_col(fd, COL_INFO)) {
! 		if (pd[1] & 0x10)
! 		    col_add_str(fd, COL_INFO, "UA F");
! 		else
! 		    col_add_str(fd, COL_INFO, "UA");
! 	    }
! 	    if (tree) {
! 		ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 						"LAPB");
! 		lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 					   pd[0], "Address: 0x%02X",
! 					   (int)pd[0]);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 					   "UA", "Unnumbered Acknowledge (UA)");
! 		proto_tree_add_text(lapb_tree, 1, 1,
! 				    "...%d.... : Final bit",
! 				    (pd[1] >> 4) & 0x1);
! 	    }
! 	    break;
! 	case LAPB_FRMR:
! 	    if (fd->flags & FROM_DCE) {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	    }
! 	    else {
! 		if(check_col(fd, COL_RES_DL_DST))
! 		    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 		if(check_col(fd, COL_RES_DL_SRC))
! 		    col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	    }
! 	    if(check_col(fd, COL_INFO)) {
! 		if (pd[1] & 0x10)
! 		    col_add_str(fd, COL_INFO, "FRMR F");
! 		else
! 		    col_add_str(fd, COL_INFO, "FRMR");
! 	    }
! 	    if (tree) {
! 		ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
! 						"LAPB");
! 		lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 					   pd[0], "Address: 0x%02X",
! 					   (int)pd[0]);
! 		proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 					   "FRMR", "Frame Reject (FRMR)");
! 		proto_tree_add_text(lapb_tree, 1, 1,
! 				    "...%d.... : Final bit",
! 				    (pd[1] >> 4) & 0x1);
! 	    }
! 	    break;
! 	}
!     }
!     else /* information frame */
!     {
! 	if (fd->flags & FROM_DCE) {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DTE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DCE");
! 	}
! 	else {
! 	    if(check_col(fd, COL_RES_DL_DST))
! 		col_add_str(fd, COL_RES_DL_DST, "DCE");
! 	    if(check_col(fd, COL_RES_DL_SRC))
! 		col_add_str(fd, COL_RES_DL_SRC, "DTE");
! 	}
! 	if(check_col(fd, COL_INFO)) {
! 	    sprintf(info, "I N(R):%d N(S):%d",
! 		    (pd[1] >> 5) & 0x7,
! 		    (pd[1] >> 1) & 0x7);
! 	    if ((pd[1] >> 4) && 0x01) /* P/F bit */
! 		strcat(info, " P");
! 	    col_add_str(fd, COL_INFO, info);
! 	}
! 	if (tree) {
! 	    ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
  					    "LAPB");
! 	    lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1,
! 				       pd[0], "Address: 0x%02X",
! 				       (int)pd[0]);
! 	    proto_tree_add_item_format(lapb_tree, hf_lapb_control, 1, 1,
! 				       "I", "Control field : 0x%02X",
! 				       (int)pd[1]);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     %d%d%d..... : N(R) = %d",
! 				(pd[1] >> 7) & 0x1,
! 				(pd[1] >> 6) & 0x1,
! 				(pd[1] >> 5) & 0x1,
! 				(pd[1] >> 5) & 0x7);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ...%d.... : Poll/Final bit",
! 				(pd[1] >> 4) & 0x1);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     ....%d%d%d. : N(S) = %d",
! 				(pd[1] >> 3) & 0x1,
! 				(pd[1] >> 2) & 0x1,
! 				(pd[1] >> 1) & 0x1,
! 				(pd[1] >> 1) & 0x7);
! 	    proto_tree_add_text(lapb_tree, 1, 1,
! 				"     .......0 : Information Transfer (I)");
! 	}
      }
  
      /* not end of frame ==> X.25 */
      if (fd->cap_len > 2) dissect_x25(pd, 2, fd, tree);
--- 46,88 ----
  dissect_lapb(const u_char *pd, frame_data *fd, proto_tree *tree)
  {
      proto_tree *lapb_tree, *ti;
!     int is_response;
  
      if (check_col(fd, COL_PROTOCOL))
  	col_add_str(fd, COL_PROTOCOL, "LAPB");
  
      if(check_col(fd, COL_RES_DL_SRC))
! 	col_add_fstr(fd, COL_RES_DL_SRC, "0x%02X", pd[0]);
!     if (fd->flags & FROM_DCE) {
! 	if(check_col(fd, COL_RES_DL_DST))
! 	    col_add_str(fd, COL_RES_DL_DST, "DTE");
! 	if(check_col(fd, COL_RES_DL_SRC))
! 	    col_add_str(fd, COL_RES_DL_SRC, "DCE");
!     }
!     else {
! 	if(check_col(fd, COL_RES_DL_DST))
! 	    col_add_str(fd, COL_RES_DL_DST, "DCE");
! 	if(check_col(fd, COL_RES_DL_SRC))
! 	    col_add_str(fd, COL_RES_DL_SRC, "DTE");
      }
  
!     if (((fd->flags & FROM_DCE) && pd[0] == 0x01) ||
!        (!(fd->flags & FROM_DCE) && pd[0] == 0x03))
! 	is_response = TRUE;
!     else
! 	is_response = FALSE;
  
!     if (tree) {
! 	ti = proto_tree_add_item_format(tree, proto_lapb, 0, 2, NULL,
  					    "LAPB");
! 	lapb_tree = proto_item_add_subtree(ti, ETT_LAPB);
! 	proto_tree_add_item_format(lapb_tree, hf_lapb_address, 0, 1, pd[0],
! 				       "Address: 0x%02X", pd[0]);
      }
+     else
+         lapb_tree = NULL;
+     dissect_xdlc_control(pd, 1, fd, lapb_tree, hf_lapb_control,
+ 	    FALSE, is_response);
  
      /* not end of frame ==> X.25 */
      if (fd->cap_len > 2) dissect_x25(pd, 2, fd, tree);
diff -c -N ../ethereal.vanilla/packet-llc.c ./packet-llc.c
*** ../ethereal.vanilla/packet-llc.c	Wed Jul 28 22:46:58 1999
--- ./packet-llc.c	Mon Aug  2 23:07:51 1999
***************
*** 34,39 ****
--- 34,40 ----
  
  #include <glib.h>
  #include "packet.h"
+ #include "xdlc.h"
  	
  static int proto_llc = -1;
  static int hf_llc_dsap = -1;
***************
*** 207,215 ****
  		llc_tree = proto_item_add_subtree(ti, ETT_LLC);
  		proto_tree_add_item(llc_tree, hf_llc_dsap, offset, 1, pd[offset]);
  		proto_tree_add_item(llc_tree, hf_llc_ssap, offset+1, 1, pd[offset+1]);
! 		proto_tree_add_item(llc_tree, hf_llc_ctrl, offset+2, 1, pd[offset+2] & 3);
! 	}
  
  	if (is_snap) {
  		if (check_col(fd, COL_INFO)) {
  			col_add_str(fd, COL_INFO, "802.2 LLC (SNAP)");
--- 208,254 ----
  		llc_tree = proto_item_add_subtree(ti, ETT_LLC);
  		proto_tree_add_item(llc_tree, hf_llc_dsap, offset, 1, pd[offset]);
  		proto_tree_add_item(llc_tree, hf_llc_ssap, offset+1, 1, pd[offset+1]);
! 	} else
! 		llc_tree = NULL;
! 
! 	/*
! 	 * XXX - we need to determine
! 	 *
! 	 *	1) whether a frame is an LLC-level command or response
! 	 *	   (RFC 1390, "Transmission of IP and ARP over FDDI
! 	 *	   Networks", says
! 	 *
! 	 *		Command frames are identified by having the
! 	 *		low order bit of the SSAP address reset to
! 	 *		zero.  Response frames have the low order
! 	 *		bit of the SSAP address set to one.
! 	 *
! 	 *	   but I don't know if that's an LLC standard, a
! 	 *	   SNAP encapsulation standard, or just some
! 	 *	   convention of RFC 1390)
! 	 *
! 	 * and
! 	 *
! 	 *	2) whether we have basic or extended operation (if
! 	 *	   we see an SABME command in the session, it's
! 	 *	   extended operation - but the capture might start
! 	 *	   after the SABME is sent, in which case we'd
! 	 *	   either have to be told by the user which it is,
! 	 *	   or somehow infer it from the contents of the
! 	 *	   packets).
! 	 */
! 	dissect_xdlc_control(pd, offset+2, fd, llc_tree, hf_llc_ctrl,
! 	    FALSE, TRUE);
  
+ 	/*
+ 	 * XXX - do we want to append the SAP information to the stuff
+ 	 * "dissect_xdlc_control()" put in the COL_INFO column, rather
+ 	 * than overwriting it?
+ 	 *
+ 	 * XXX - we shouldn't, as far as I know, pass S frames to
+ 	 * "ethertype" or "dissect", and we may have to treat I frames
+ 	 * differently from U frames.
+ 	 */
  	if (is_snap) {
  		if (check_col(fd, COL_INFO)) {
  			col_add_str(fd, COL_INFO, "802.2 LLC (SNAP)");
diff -c -N ../ethereal.vanilla/packet.h ./packet.h
*** ../ethereal.vanilla/packet.h	Sun Aug  1 19:04:26 1999
--- ./packet.h	Mon Aug  2 21:10:21 1999
***************
*** 297,302 ****
--- 297,303 ----
  	ETT_RADIUS_AVP,
  	ETT_LAPB,
  	ETT_X25,
+ 	ETT_XDLC_CONTROL,
  	NUM_TREE_TYPES	/* last item number plus one */
  };
  
diff -c -N ../ethereal.vanilla/xdlc.c ./xdlc.c
*** ../ethereal.vanilla/xdlc.c	Wed Dec 31 16:00:00 1969
--- ./xdlc.c	Mon Aug  2 23:09:15 1999
***************
*** 0 ****
--- 1,354 ----
+ /* xdlc.c
+  * Routines for use by various SDLC-derived protocols, such as HDLC
+  * and its derivatives LAPB, IEEE 802.2 LLC, etc..
+  *
+  * $Id$
+  *
+  * Ethereal - Network traffic analyzer
+  * By Gerald Combs <gerald@xxxxxxxxxx>
+  * Copyright 1998 Gerald Combs
+  *
+  * 
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  * 
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  */
+ 
+ #ifdef HAVE_CONFIG_H
+ # include "config.h"
+ #endif
+ 
+ #ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ 
+ #include <stdio.h>
+ #include <string.h>
+ 
+ #include <glib.h>
+ #include "packet.h"
+ #include "xdlc.h"
+ 	
+ /*
+  * N(S) and N(R) fields, in basic and extended operation.
+  */
+ #define XDLC_N_R_MASK		0xE0	/* basic */
+ #define XDLC_N_R_SHIFT		5
+ #define XDLC_N_R_EXT_MASK	0xFE00	/* extended */
+ #define XDLC_N_R_EXT_SHIFT	9
+ #define XDLC_N_S_MASK		0x0E	/* basic */
+ #define XDLC_N_S_SHIFT		1
+ #define XDLC_N_S_EXT_MASK	0x00FE	/* extended */
+ #define XDLC_N_S_EXT_SHIFT	1
+ 
+ /*
+  * Poll/Final bit, in basic and extended operation.
+  */
+ #define XDLC_P_F	0x10	/* basic */
+ #define XDLC_P_F_EXT	0x0100	/* extended */
+ 
+ /*
+  * S-format frame types.
+  */
+ #define XDLC_S_FTYPE_MASK	0x0C
+ #define XDLC_RR			0x00	/* Receiver ready */
+ #define XDLC_RNR		0x04	/* Receiver not ready */
+ #define XDLC_REJ		0x08	/* Reject */
+ #define XDLC_SREJ		0x0C	/* Selective reject */
+ 
+ static const value_string stype_vals[] = {
+     { XDLC_RR,   "Receiver ready" },
+     { XDLC_RNR,  "Receiver not ready" },
+     { XDLC_REJ,  "Reject" },
+     { XDLC_SREJ, "Selective reject" },
+     { 0,         NULL }
+ };
+ 
+ /*
+  * U-format modifiers.
+  */
+ #define XDLC_U_MODIFIER_MASK	0xEC
+ #define XDLC_UI		0x00	/* Unnumbered Information */
+ #define XDLC_UP		0x20	/* Unnumbered Poll */
+ #define XDLC_DISC	0x40	/* Disconnect */
+ #define XDLC_UA		0x60	/* Unnumbered Acknowledge */
+ #define XDLC_SNRM	0x80	/* Set Normal Response Mode */
+ #define XDLC_TEST	0xC0	/* Test */
+ #define XDLC_RIM	0x04	/* Request Initialization Mode */
+ #define XDLC_SIM	0x44	/* Set Initialization Mode */
+ #define XDLC_FRMR	0x84	/* Frame reject */
+ #define XDLC_CFGR	0xC4	/* Configure */
+ #define XDLC_DM		0x0C	/* Disconnected mode */
+ #define XDLC_SARM	0x0C	/* Set Asynchronous Response Mode */
+ #define XDLC_SABM	0x2C	/* Set Asynchronous Balanced Mode */
+ #define XDLC_SARME	0x4C	/* Set Asynchronous Response Mode Extended */
+ #define XDLC_SABME	0x6C	/* Set Asynchronous Balanced Mode Extended */
+ #define XDLC_RESET	0x8C	/* Reset */
+ #define XDLC_SNRME	0xCC	/* Set Normal Response Mode Extended */
+ #define XDLC_BCN	0xEC	/* Beacon */
+ 
+ static const value_string modifier_short_vals[] = {
+     { XDLC_UI,    "UI" },
+     { XDLC_UP,    "UP" },
+     { XDLC_DISC,  "DISC" },
+     { XDLC_UA,    "UA" },
+     { XDLC_SNRM,  "SNRM" },
+     { XDLC_TEST,  "TEST" },
+     { XDLC_RIM,   "RIM" },
+     { XDLC_SIM,   "SIM" },
+     { XDLC_FRMR,  "FRMR" },
+     { XDLC_CFGR,  "CFGR" },
+     { XDLC_DM,    "DM" },	/* XXX - same as SARM */
+     { XDLC_SARM,  "SARM" },
+     { XDLC_SABM,  "SABM" },
+     { XDLC_SARME, "SARME" },
+     { XDLC_SABME, "SABME" },
+     { XDLC_RESET, "RESET" },
+     { XDLC_SNRME, "SNRME" },
+     { XDLC_BCN,   "BCN" },
+     { 0,          NULL }
+ };
+ 
+ static const value_string modifier_vals[] = {
+     { XDLC_UI,    "Unnumbered Information" },
+     { XDLC_UP,    "Unnumbered Poll" },
+     { XDLC_DISC,  "Disconnect" },
+     { XDLC_UA,    "Unnumbered Acknowledge" },
+     { XDLC_SNRM,  "Set Normal Response Mode" },
+     { XDLC_TEST,  "Test" },
+     { XDLC_RIM,   "Request Initialization Mode" },
+     { XDLC_SIM,   "Set Initialization Mode" },
+     { XDLC_FRMR,  "Frame reject" },
+     { XDLC_CFGR,  "Configure" },
+     { XDLC_DM,    "Disconnected mode" },	/* XXX - same as SARM */
+     { XDLC_SARM,  "Set Asynchronous Response Mode" },
+     { XDLC_SABM,  "Set Asynchronous Balanced Mode" },
+     { XDLC_SARME, "Set Asynchronous Response Mode Extended" },
+     { XDLC_SABME, "Set Asynchronous Balanced Mode Extended" },
+     { XDLC_RESET, "Reset" },
+     { XDLC_SNRME, "Set Normal Response Mode Extended" },
+     { XDLC_BCN,   "Beacon" },
+     { 0,          NULL }
+ };
+ 
+ /*
+  * XXX - is "is_response" determined by the modifier value?
+  */
+ int
+ dissect_xdlc_control(const u_char *pd, int offset, frame_data *fd,
+   proto_tree *xdlc_tree, int hf_xdlc_control, 
+   int is_response, int is_extended)
+ {
+     guint16 control;
+     char info[80];
+     proto_tree *tc, *control_tree;
+     gchar *frame_type = NULL;
+     gchar *modifier;
+ 
+     switch (pd[offset] & 0x03) {
+ 
+     case XDLC_S:
+         /*
+ 	 * Supervisory frame.
+ 	 */
+ 	if (is_extended)
+ 		control = pletohs(&pd[offset]);
+ 	else
+ 		control = pd[offset];
+ 	switch (control & XDLC_S_FTYPE_MASK) {
+ 	case XDLC_RR:
+ 	    frame_type = "RR";
+ 	    break;
+ 
+ 	case XDLC_RNR:
+ 	    frame_type = "RNR";
+ 	    break;
+ 
+ 	case XDLC_REJ:
+ 	    frame_type = "REJ";
+ 	    break;
+ 
+ 	case XDLC_SREJ:
+ 	    frame_type = "SREJ";
+ 	    break;
+ 	}
+ 	if (is_extended) {
+ 	    sprintf(info, "S%s, func = %s, N(R) = %u", frame_type,
+ 		 	((control & XDLC_P_F_EXT) ?
+ 		 	    (is_response ? " F" : " P") :
+ 		 	    ""),
+ 			(control & XDLC_N_R_EXT_MASK) >> XDLC_N_R_EXT_SHIFT);
+ 	} else {
+ 	    sprintf(info, "S%s, func = %s, N(R) = %u", frame_type,
+ 		 	((control & XDLC_P_F) ?
+ 		 	    (is_response ? " F" : " P") :
+ 		 	    ""),
+ 			(control & XDLC_N_R_MASK) >> XDLC_N_R_SHIFT);
+ 	}
+ 	if (check_col(fd, COL_INFO))
+ 	    col_add_str(fd, COL_INFO, info);
+ 	if (xdlc_tree) {
+ 	    if (is_extended) {
+ 		tc = proto_tree_add_item_format(xdlc_tree, hf_xdlc_control,
+ 			offset, 2,
+ 			frame_type,
+ 			"Control field: %s (0x%04X)", info, control);
+ 		control_tree = proto_item_add_subtree(tc, ETT_XDLC_CONTROL);
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_numeric_bitfield(control, XDLC_N_R_EXT_MASK, 2*8,
+ 			"N(R) = %u"));
+ 		if (control & XDLC_P_F_EXT) {
+ 		    proto_tree_add_text(control_tree, offset, 2,
+ 			decode_boolean_bitfield(control, XDLC_P_F_EXT, 2*8,
+ 		  	    (is_response ? "Final" : "Poll"), NULL));
+ 		}
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_enumerated_bitfield(control, XDLC_S_FTYPE_MASK, 2*8,
+ 			stype_vals, "Supervisory frame - %s"));
+ 		/* This will always say it's a supervisory frame */
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_boolean_bitfield(control, 0x03, 2*8,
+ 			"Supervisory frame", NULL));
+ 	    } else {
+ 		tc = proto_tree_add_item_format(xdlc_tree, hf_xdlc_control,
+ 			offset, 1,
+ 			frame_type,
+ 			"Control field: %s (0x%02X)", info, control);
+ 		control_tree = proto_item_add_subtree(tc, ETT_XDLC_CONTROL);
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_numeric_bitfield(control, XDLC_N_R_MASK, 1*8,
+ 			"N(R) = %u"));
+ 		if (control & XDLC_P_F) {
+ 		    proto_tree_add_text(control_tree, offset, 1,
+ 			decode_boolean_bitfield(control, XDLC_P_F, 1*8,
+ 		  	    (is_response ? "Final" : "Poll"), NULL));
+ 		}
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_enumerated_bitfield(control, XDLC_S_FTYPE_MASK, 1*8,
+ 			stype_vals, "%s"));
+ 		/* This will always say it's a supervisory frame */
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_boolean_bitfield(control, 0x03, 1*8,
+ 			"Supervisory frame", NULL));
+ 	    }
+ 	}
+ 	return XDLC_S;
+ 
+     case XDLC_U:
+ 	/*
+ 	 * Unnumbered frame.
+ 	 *
+ 	 * XXX - is this two octets, with a P/F bit, in HDLC extended
+ 	 * operation?  It's one octet in LLC, even though the control
+ 	 * field of I and S frames is a 2-byte extended-operation field
+ 	 * in LLC.  Given that there are no sequence numbers in the
+ 	 * control field of a U frame, there doesn't appear to be any
+ 	 * need for it to be 2 bytes in extended operation.
+ 	 */
+ 	control = pd[offset];
+ 	modifier = match_strval(control & XDLC_U_MODIFIER_MASK,
+ 			modifier_short_vals);
+ 	if (modifier == NULL)
+ 		modifier = "Unknown";
+ 	sprintf(info, "U%s, func = %s",
+ 		((control & XDLC_P_F) ? " P" : ""), modifier);
+ 	if (check_col(fd, COL_INFO))
+ 	    col_add_str(fd, COL_INFO, info);
+ 	if (xdlc_tree) {
+ 	    tc = proto_tree_add_item_format(xdlc_tree, hf_xdlc_control,
+ 			offset, 1,
+ 			frame_type,
+ 			"Control field: %s (0x%02X)", info, control);
+ 	    control_tree = proto_item_add_subtree(tc, ETT_XDLC_CONTROL);
+ 	    if (control & XDLC_P_F) {
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_boolean_bitfield(control, XDLC_P_F, 1*8,
+ 			"Poll", NULL));
+ 	    }
+ 	    proto_tree_add_text(control_tree, offset, 1,
+ 		decode_enumerated_bitfield(control, XDLC_U_MODIFIER_MASK, 1*8,
+ 		    modifier_vals, "%s"));
+ 	    /* This will always say it's an unnumbered frame */
+ 	    proto_tree_add_text(control_tree, offset, 1,
+ 		decode_boolean_bitfield(control, 0x03, 1*8,
+ 		    "Unnumbered frame", NULL));
+ 	}
+ 	return XDLC_U;
+ 
+     default:
+ 	/*
+ 	 * Information frame.
+ 	 */
+ 	control = pd[offset];
+ 	if (is_extended) {
+ 	    sprintf(info, "I%s, N(R) = %u, N(S) = %u",
+ 			((control & XDLC_P_F_EXT) ? " P" : ""),
+ 			(control & XDLC_N_R_EXT_MASK) >> XDLC_N_R_EXT_SHIFT,
+ 			(control & XDLC_N_S_EXT_MASK) >> XDLC_N_S_EXT_SHIFT);
+ 	} else {
+ 	    sprintf(info, "I%s, N(R) = %u, N(S) = %u",
+ 			((control & XDLC_P_F) ? " P" : ""),
+ 			(control & XDLC_N_R_MASK) >> XDLC_N_R_SHIFT,
+ 			(control & XDLC_N_S_MASK) >> XDLC_N_S_SHIFT);
+ 	}
+ 	if (check_col(fd, COL_INFO))
+ 	    col_add_str(fd, COL_INFO, info);
+ 	if (xdlc_tree) {
+ 	    tc = proto_tree_add_item_format(xdlc_tree, hf_xdlc_control,
+ 			offset, 2,
+ 			frame_type,
+ 			"Control field: %s (0x%04X)", info, control);
+ 	    control_tree = proto_item_add_subtree(tc, ETT_XDLC_CONTROL);
+ 	    if (is_extended) {
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_numeric_bitfield(control, XDLC_N_R_EXT_MASK, 2*8,
+ 		  		"N(R) = %u"));
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_numeric_bitfield(control, XDLC_N_S_EXT_MASK, 2*8,
+ 		  		"N(S) = %u"));
+ 		if (control & XDLC_P_F_EXT) {
+ 		    proto_tree_add_text(control_tree, offset, 2,
+ 			decode_boolean_bitfield(control, XDLC_P_F_EXT, 2*8,
+ 		  		"Poll", NULL));
+ 		}
+ 		/* This will always say it's an information frame */
+ 		proto_tree_add_text(control_tree, offset, 2,
+ 		    decode_boolean_bitfield(control, 0x01, 2*8,
+ 			NULL, "Information frame"));
+ 	    } else {
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_numeric_bitfield(control, XDLC_N_R_MASK, 1*8,
+ 		  		"N(R) = %u"));
+ 		/* XXX - make it shift the value appropriately! */
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_numeric_bitfield(control, XDLC_N_S_MASK, 1*8,
+ 		  		"N(S) = %u"));
+ 		if (control & XDLC_P_F) {
+ 		    proto_tree_add_text(control_tree, offset, 1,
+ 			decode_boolean_bitfield(control, XDLC_P_F, 1*8,
+ 		  		"Poll", NULL));
+ 		}
+ 		/* This will always say it's an information frame */
+ 		proto_tree_add_text(control_tree, offset, 1,
+ 		    decode_boolean_bitfield(control, 0x01, 1*8,
+ 			NULL, "Information frame"));
+ 	    }
+ 	}
+ 	return XDLC_I;
+     }
+ }
diff -c -N ../ethereal.vanilla/xdlc.h ./xdlc.h
*** ../ethereal.vanilla/xdlc.h	Wed Dec 31 16:00:00 1969
--- ./xdlc.h	Mon Aug  2 23:06:13 1999
***************
*** 0 ****
--- 1,36 ----
+ /* xdlc.h
+  * Define *DLC frame types, and routine to dissect the control field of
+  * a *DLC frame.
+  *
+  * $Id$
+  *
+  * Ethereal - Network traffic analyzer
+  * By Gerald Combs <gerald@xxxxxxxxxx>
+  * Copyright 1998 Gerald Combs
+  *
+  * 
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version.
+  * 
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  * 
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+  */
+ 
+ /*
+  * Low-order bits of first (extended) or only (basic) octet of control
+  * field, specifying the frame type.
+  */
+ #define XDLC_I		0x00	/* Information frames */
+ #define XDLC_S		0x01	/* Supervisory frames */
+ #define XDLC_U		0x03	/* Unnumbered frames */
+ 
+ int dissect_xdlc_control(const u_char *pd, int offset, frame_data *fd,
+   proto_tree *xdlc_tree, int hf_xdlc_control, int is_response, int extended);