示例#1
0
int
llc_print(const u_char *p, u_int length, u_int caplen,
          const u_char *esrc, const u_char *edst, u_short *extracted_ethertype)
{
    u_int8_t dsap_field, dsap, ssap_field, ssap;
    u_int16_t control;
    int is_u;
    register int ret;

    *extracted_ethertype = 0;

    if (caplen < 3) {
        (void)printf("[|llc]");
        default_print((u_char *)p, caplen);
        return(0);
    }

    dsap_field = *p;
    ssap_field = *(p + 1);

    /*
     * OK, what type of LLC frame is this?  The length
     * of the control field depends on that - I frames
     * have a two-byte control field, and U frames have
     * a one-byte control field.
     */
    control = *(p + 2);
    if ((control & LLC_U_FMT) == LLC_U_FMT) {
        /*
         * U frame.
         */
        is_u = 1;
    } else {
        /*
         * The control field in I and S frames is
         * 2 bytes...
         */
        if (caplen < 4) {
            (void)printf("[|llc]");
            default_print((u_char *)p, caplen);
            return(0);
        }

        /*
         * ...and is little-endian.
         */
        control = EXTRACT_LE_16BITS(p + 2);
        is_u = 0;
    }

    if (ssap_field == LLCSAP_GLOBAL && dsap_field == LLCSAP_GLOBAL) {
        /*
         * This is an Ethernet_802.3 IPX frame; it has an
         * 802.3 header (i.e., an Ethernet header where the
         * type/length field is <= ETHERMTU, i.e. it's a length
         * field, not a type field), but has no 802.2 header -
         * the IPX packet starts right after the Ethernet header,
         * with a signature of two bytes of 0xFF (which is
         * LLCSAP_GLOBAL).
         *
         * (It might also have been an Ethernet_802.3 IPX at
         * one time, but got bridged onto another network,
         * such as an 802.11 network; this has appeared in at
         * least one capture file.)
         */

        if (eflag)
            printf("IPX 802.3: ");

        ipx_print(p, length);
        return (1);
    }

    dsap = dsap_field & ~LLC_IG;
    ssap = ssap_field & ~LLC_GSAP;

    if (eflag) {
        printf("LLC, dsap %s (0x%02x) %s, ssap %s (0x%02x) %s",
               tok2str(llc_values, "Unknown", dsap),
               dsap,
               tok2str(llc_ig_flag_values, "Unknown", dsap_field & LLC_IG),
               tok2str(llc_values, "Unknown", ssap),
               ssap,
               tok2str(llc_flag_values, "Unknown", ssap_field & LLC_GSAP));

        if (is_u) {
            printf(", ctrl 0x%02x: ", control);
        } else {
            printf(", ctrl 0x%04x: ", control);
        }
    }

    if (ssap == LLCSAP_8021D && dsap == LLCSAP_8021D &&
            control == LLC_UI) {
        stp_print(p+3, length-3);
        return (1);
    }

    if (ssap == LLCSAP_IP && dsap == LLCSAP_IP &&
            control == LLC_UI) {
        ip_print(gndo, p+4, length-4);
        return (1);
    }

    if (ssap == LLCSAP_IPX && dsap == LLCSAP_IPX &&
            control == LLC_UI) {
        /*
         * This is an Ethernet_802.2 IPX frame, with an 802.3
         * header and an 802.2 LLC header with the source and
         * destination SAPs being the IPX SAP.
         *
         * Skip DSAP, LSAP, and control field.
         */
        if (eflag)
            printf("IPX 802.2: ");

        ipx_print(p+3, length-3);
        return (1);
    }

#ifdef TCPDUMP_DO_SMB
    if (ssap == LLCSAP_NETBEUI && dsap == LLCSAP_NETBEUI
            && (!(control & LLC_S_FMT) || control == LLC_U_FMT)) {
        /*
         * we don't actually have a full netbeui parser yet, but the
         * smb parser can handle many smb-in-netbeui packets, which
         * is very useful, so we call that
         *
         * We don't call it for S frames, however, just I frames
         * (which are frames that don't have the low-order bit,
         * LLC_S_FMT, set in the first byte of the control field)
         * and UI frames (whose control field is just 3, LLC_U_FMT).
         */

        /*
         * Skip the LLC header.
         */
        if (is_u) {
            p += 3;
            length -= 3;
            caplen -= 3;
        } else {
            p += 4;
            length -= 4;
            caplen -= 4;
        }
        netbeui_print(control, p, length);
        return (1);
    }
#endif
    if (ssap == LLCSAP_ISONS && dsap == LLCSAP_ISONS
            && control == LLC_UI) {
        isoclns_print(p + 3, length - 3, caplen - 3);
        return (1);
    }

    if (ssap == LLCSAP_SNAP && dsap == LLCSAP_SNAP
            && control == LLC_UI) {
        /*
         * XXX - what *is* the right bridge pad value here?
         * Does anybody ever bridge one form of LAN traffic
         * over a networking type that uses 802.2 LLC?
         */
        ret = snap_print(p+3, length-3, caplen-3, extracted_ethertype,
                         2);
        if (ret)
            return (ret);
    }

    if (!eflag) {
        if (ssap == dsap) {
            if (esrc == NULL || edst == NULL)
                (void)printf("%s ", tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
            else
                (void)printf("%s > %s %s ",
                             etheraddr_string(esrc),
                             etheraddr_string(edst),
                             tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
        } else {
            if (esrc == NULL || edst == NULL)
                (void)printf("%s > %s ",
                             tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
                             tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
            else
                (void)printf("%s %s > %s %s ",
                             etheraddr_string(esrc),
                             tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
                             etheraddr_string(edst),
                             tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
        }
    }

    if (is_u) {
        printf("Unnumbered, %s, Flags [%s], length %u",
               tok2str(llc_cmd_values, "%02x", LLC_U_CMD(control)),
               tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_U_POLL)),
               length);

        p += 3;
        length -= 3;
        caplen -= 3;

        if ((control & ~LLC_U_POLL) == LLC_XID) {
            if (*p == LLC_XID_FI) {
                printf(": %02x %02x", p[1], p[2]);
                p += 3;
                length -= 3;
                caplen -= 3;
            }
        }
    } else {
        if ((control & LLC_S_FMT) == LLC_S_FMT) {
            (void)printf("Supervisory, %s, rcv seq %u, Flags [%s], length %u",
                         tok2str(llc_supervisory_values,"?",LLC_S_CMD(control)),
                         LLC_IS_NR(control),
                         tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
                         length);
        } else {
            (void)printf("Information, send seq %u, rcv seq %u, Flags [%s], length %u",
                         LLC_I_NS(control),
                         LLC_IS_NR(control),
                         tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
                         length);
        }
        p += 4;
        length -= 4;
        caplen -= 4;
    }
    return(1);
}
示例#2
0
文件: print-llc.c 项目: OPSF/uClinux
/*
 * Returns non-zero IFF it succeeds in printing the header
 */
int
llc_print(const u_char *p, u_int length, u_int caplen,
	  const u_char *esrc, const u_char *edst, u_short *extracted_ethertype)
{
	u_int8_t dsap, ssap;
	u_int16_t control;
	int is_u;
	register int ret;

	if (caplen < 3) {
		(void)printf("[|llc]");
		default_print((u_char *)p, caplen);
		return(0);
	}

	dsap = *p;
	ssap = *(p + 1);

	/*
	 * OK, what type of LLC frame is this?  The length
	 * of the control field depends on that - I frames
	 * have a two-byte control field, and U frames have
	 * a one-byte control field.
	 */
	control = *(p + 2);
	if ((control & LLC_U_FMT) == LLC_U_FMT) {
		/*
		 * U frame.
		 */
		is_u = 1;
	} else {
		/*
		 * The control field in I and S frames is
		 * 2 bytes...
		 */
		if (caplen < 4) {
			(void)printf("[|llc]");
			default_print((u_char *)p, caplen);
			return(0);
		}

		/*
		 * ...and is little-endian.
		 */
		control = EXTRACT_LE_16BITS(p + 2);
		is_u = 0;
	}

#if !defined(EMBED) || defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
	if (ssap == LLCSAP_GLOBAL && dsap == LLCSAP_GLOBAL) {
		/*
		 * This is an Ethernet_802.3 IPX frame; it has an
		 * 802.3 header (i.e., an Ethernet header where the
		 * type/length field is <= ETHERMTU, i.e. it's a length
		 * field, not a type field), but has no 802.2 header -
		 * the IPX packet starts right after the Ethernet header,
		 * with a signature of two bytes of 0xFF (which is
		 * LLCSAP_GLOBAL).
		 *
		 * (It might also have been an Ethernet_802.3 IPX at
		 * one time, but got bridged onto another network,
		 * such as an 802.11 network; this has appeared in at
		 * least one capture file.)
		 */

            if (eflag)
		printf("IPX-802.3: ");

            ipx_print(p, length);
            return (1);
	}
#endif

	if (eflag) {
		if (is_u) {
			printf("LLC, dsap %s (0x%02x), ssap %s (0x%02x), cmd 0x%02x: ",
			    tok2str(llc_values, "Unknown", dsap),
			    dsap,
			    tok2str(llc_values, "Unknown", ssap),
			    ssap,
			    control);
		} else {
			printf("LLC, dsap %s (0x%02x), ssap %s (0x%02x), cmd 0x%04x: ",
			    tok2str(llc_values, "Unknown", dsap),
			    dsap,
			    tok2str(llc_values, "Unknown", ssap),
			    ssap,
			    control);
		}
	}

#if !defined(EMBED) || defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
	if (ssap == LLCSAP_8021D && dsap == LLCSAP_8021D &&
	    control == LLC_UI) {
		stp_print(p+3, length-3);
		return (1);
	}
#endif

	if (ssap == LLCSAP_IP && dsap == LLCSAP_IP &&
	    control == LLC_UI) {
		ip_print(gndo, p+4, length-4);
		return (1);
	}

#if !defined(EMBED) || defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
	if (ssap == LLCSAP_IPX && dsap == LLCSAP_IPX &&
	    control == LLC_UI) {
		/*
		 * This is an Ethernet_802.2 IPX frame, with an 802.3
		 * header and an 802.2 LLC header with the source and
		 * destination SAPs being the IPX SAP.
		 *
		 * Skip DSAP, LSAP, and control field.
		 */
		printf("(NOV-802.2) ");
		ipx_print(p+3, length-3);
		return (1);
	}
#endif

#ifdef TCPDUMP_DO_SMB
	if (ssap == LLCSAP_NETBEUI && dsap == LLCSAP_NETBEUI
	    && (!(control & LLC_S_FMT) || control == LLC_U_FMT)) {
		/*
		 * we don't actually have a full netbeui parser yet, but the
		 * smb parser can handle many smb-in-netbeui packets, which
		 * is very useful, so we call that
		 *
		 * We don't call it for S frames, however, just I frames
		 * (which are frames that don't have the low-order bit,
		 * LLC_S_FMT, set in the first byte of the control field)
		 * and UI frames (whose control field is just 3, LLC_U_FMT).
		 */

		/*
		 * Skip the LLC header.
		 */
		if (is_u) {
			p += 3;
			length -= 3;
			caplen -= 3;
		} else {
			p += 4;
			length -= 4;
			caplen -= 4;
		}
		netbeui_print(control, p, length);
		return (1);
	}
#endif
#if !defined(EMBED)
	if (ssap == LLCSAP_ISONS && dsap == LLCSAP_ISONS
	    && control == LLC_UI) {
		isoclns_print(p + 3, length - 3, caplen - 3);
		return (1);
	}
#endif

	if (ssap == LLCSAP_SNAP && dsap == LLCSAP_SNAP
	    && control == LLC_UI) {
		/*
		 * XXX - what *is* the right bridge pad value here?
		 * Does anybody ever bridge one form of LAN traffic
		 * over a networking type that uses 802.2 LLC?
		 */
		ret = snap_print(p+3, length-3, caplen-3, extracted_ethertype,
		    2);
		if (ret)
			return (ret);
	}

	if (!eflag) {
		if ((ssap & ~LLC_GSAP) == dsap) {
			if (esrc == NULL || edst == NULL)
				(void)printf("%s ", llcsap_string(dsap));
			else
				(void)printf("%s > %s %s ",
						etheraddr_string(esrc),
						etheraddr_string(edst),
						llcsap_string(dsap));
		} else {
			if (esrc == NULL || edst == NULL)
				(void)printf("%s > %s ",
					llcsap_string(ssap & ~LLC_GSAP),
					llcsap_string(dsap));
			else
				(void)printf("%s %s > %s %s ",
					etheraddr_string(esrc),
					llcsap_string(ssap & ~LLC_GSAP),
					etheraddr_string(edst),
					llcsap_string(dsap));
		}
	}

	if (is_u) {
		const char *m;
		char f;

		m = tok2str(cmd2str, "%02x", LLC_U_CMD(control));
		switch ((ssap & LLC_GSAP) | (control & LLC_U_POLL)) {
			case 0:			f = 'C'; break;
			case LLC_GSAP:		f = 'R'; break;
			case LLC_U_POLL:	f = 'P'; break;
			case LLC_GSAP|LLC_U_POLL: f = 'F'; break;
			default:		f = '?'; break;
		}

		printf("%s/%c", m, f);

		p += 3;
		length -= 3;
		caplen -= 3;

		if ((control & ~LLC_U_POLL) == LLC_XID) {
			if (*p == LLC_XID_FI) {
				printf(": %02x %02x", p[1], p[2]);
				p += 3;
				length -= 3;
				caplen -= 3;
			}
		}
	} else {
		char f;

		switch ((ssap & LLC_GSAP) | (control & LLC_IS_POLL)) {
			case 0:			f = 'C'; break;
			case LLC_GSAP:		f = 'R'; break;
			case LLC_IS_POLL:	f = 'P'; break;
			case LLC_GSAP|LLC_IS_POLL: f = 'F'; break;
			default:		f = '?'; break;
		}

		if ((control & LLC_S_FMT) == LLC_S_FMT) {
			static const char *llc_s[] = { "rr", "rej", "rnr", "03" };
			(void)printf("%s (r=%d,%c)",
				llc_s[LLC_S_CMD(control)],
				LLC_IS_NR(control),
				f);
		} else {
			(void)printf("I (s=%d,r=%d,%c)",
				LLC_I_NS(control),
				LLC_IS_NR(control),
				f);
		}
		p += 4;
		length -= 4;
		caplen -= 4;
	}
	return(1);
}
示例#3
0
/*
 * If we printed information about the payload, returns the length of the LLC
 * header, plus the length of any SNAP header following it.
 *
 * Otherwise (for example, if the packet has unknown SAPs or has a SNAP
 * header with an unknown OUI/PID combination), returns the *negative*
 * of that value.
 */
int
llc_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen,
	  const struct lladdr_info *src, const struct lladdr_info *dst)
{
	uint8_t dsap_field, dsap, ssap_field, ssap;
	uint16_t control;
	int hdrlen;
	int is_u;

	if (caplen < 3) {
		ND_PRINT("[|llc]");
		ND_DEFAULTPRINT((const u_char *)p, caplen);
		return (caplen);
	}
	if (length < 3) {
		ND_PRINT("[|llc]");
		ND_DEFAULTPRINT((const u_char *)p, caplen);
		return (length);
	}

	dsap_field = EXTRACT_U_1(p);
	ssap_field = EXTRACT_U_1(p + 1);

	/*
	 * OK, what type of LLC frame is this?  The length
	 * of the control field depends on that - I frames
	 * have a two-byte control field, and U frames have
	 * a one-byte control field.
	 */
	control = EXTRACT_U_1(p + 2);
	if ((control & LLC_U_FMT) == LLC_U_FMT) {
		/*
		 * U frame.
		 */
		is_u = 1;
		hdrlen = 3;	/* DSAP, SSAP, 1-byte control field */
	} else {
		/*
		 * The control field in I and S frames is
		 * 2 bytes...
		 */
		if (caplen < 4) {
			ND_PRINT("[|llc]");
			ND_DEFAULTPRINT((const u_char *)p, caplen);
			return (caplen);
		}
		if (length < 4) {
			ND_PRINT("[|llc]");
			ND_DEFAULTPRINT((const u_char *)p, caplen);
			return (length);
		}

		/*
		 * ...and is little-endian.
		 */
		control = EXTRACT_LE_U_2(p + 2);
		is_u = 0;
		hdrlen = 4;	/* DSAP, SSAP, 2-byte control field */
	}

	if (ssap_field == LLCSAP_GLOBAL && dsap_field == LLCSAP_GLOBAL) {
		/*
		 * This is an Ethernet_802.3 IPX frame; it has an
		 * 802.3 header (i.e., an Ethernet header where the
		 * type/length field is <= MAX_ETHERNET_LENGTH_VAL,
		 * i.e. it's a length field, not a type field), but
		 * has no 802.2 header - the IPX packet starts right
		 * after the Ethernet header, with a signature of two
		 * bytes of 0xFF (which is LLCSAP_GLOBAL).
		 *
		 * (It might also have been an Ethernet_802.3 IPX at
		 * one time, but got bridged onto another network,
		 * such as an 802.11 network; this has appeared in at
		 * least one capture file.)
		 */

            if (ndo->ndo_eflag)
		ND_PRINT("IPX 802.3: ");

            ipx_print(ndo, p, length);
            return (0);		/* no LLC header */
	}

	dsap = dsap_field & ~LLC_IG;
	ssap = ssap_field & ~LLC_GSAP;

	if (ndo->ndo_eflag) {
                ND_PRINT("LLC, dsap %s (0x%02x) %s, ssap %s (0x%02x) %s",
                       tok2str(llc_values, "Unknown", dsap),
                       dsap,
                       tok2str(llc_ig_flag_values, "Unknown", dsap_field & LLC_IG),
                       tok2str(llc_values, "Unknown", ssap),
                       ssap,
                       tok2str(llc_flag_values, "Unknown", ssap_field & LLC_GSAP));

		if (is_u) {
			ND_PRINT(", ctrl 0x%02x: ", control);
		} else {
			ND_PRINT(", ctrl 0x%04x: ", control);
		}
	}

	/*
	 * Skip LLC header.
	 */
	p += hdrlen;
	length -= hdrlen;
	caplen -= hdrlen;

	if (ssap == LLCSAP_SNAP && dsap == LLCSAP_SNAP
	    && control == LLC_UI) {
		/*
		 * XXX - what *is* the right bridge pad value here?
		 * Does anybody ever bridge one form of LAN traffic
		 * over a networking type that uses 802.2 LLC?
		 */
		if (!snap_print(ndo, p, length, caplen, src, dst, 2)) {
			/*
			 * Unknown packet type; tell our caller, by
			 * returning a negative value, so they
			 * can print the raw packet.
			 */
			return (-(hdrlen + 5));	/* include LLC and SNAP header */
		} else
			return (hdrlen + 5);	/* include LLC and SNAP header */
	}

	if (ssap == LLCSAP_8021D && dsap == LLCSAP_8021D &&
	    control == LLC_UI) {
		stp_print(ndo, p, length);
		return (hdrlen);
	}

	if (ssap == LLCSAP_IP && dsap == LLCSAP_IP &&
	    control == LLC_UI) {
		/*
		 * This is an RFC 948-style IP packet, with
		 * an 802.3 header and an 802.2 LLC header
		 * with the source and destination SAPs being
		 * the IP SAP.
		 */
		ip_print(ndo, p, length);
		return (hdrlen);
	}

	if (ssap == LLCSAP_IPX && dsap == LLCSAP_IPX &&
	    control == LLC_UI) {
		/*
		 * This is an Ethernet_802.2 IPX frame, with an 802.3
		 * header and an 802.2 LLC header with the source and
		 * destination SAPs being the IPX SAP.
		 */
                if (ndo->ndo_eflag)
                        ND_PRINT("IPX 802.2: ");

		ipx_print(ndo, p, length);
		return (hdrlen);
	}

#ifdef ENABLE_SMB
	if (ssap == LLCSAP_NETBEUI && dsap == LLCSAP_NETBEUI
	    && (!(control & LLC_S_FMT) || control == LLC_U_FMT)) {
		/*
		 * we don't actually have a full netbeui parser yet, but the
		 * smb parser can handle many smb-in-netbeui packets, which
		 * is very useful, so we call that
		 *
		 * We don't call it for S frames, however, just I frames
		 * (which are frames that don't have the low-order bit,
		 * LLC_S_FMT, set in the first byte of the control field)
		 * and UI frames (whose control field is just 3, LLC_U_FMT).
		 */
		netbeui_print(ndo, control, p, length);
		return (hdrlen);
	}
#endif
	if (ssap == LLCSAP_ISONS && dsap == LLCSAP_ISONS
	    && control == LLC_UI) {
		isoclns_print(ndo, p, length);
		return (hdrlen);
	}

	if (!ndo->ndo_eflag) {
		if (ssap == dsap) {
			if (src == NULL || dst == NULL)
				ND_PRINT("%s ", tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
			else
				ND_PRINT("%s > %s %s ",
						(src->addr_string)(ndo, src->addr),
						(dst->addr_string)(ndo, dst->addr),
						tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
		} else {
			if (src == NULL || dst == NULL)
				ND_PRINT("%s > %s ",
                                        tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
					tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
			else
				ND_PRINT("%s %s > %s %s ",
					(src->addr_string)(ndo, src->addr),
                                        tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
					(dst->addr_string)(ndo, dst->addr),
					tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
		}
	}

	if (is_u) {
		ND_PRINT("Unnumbered, %s, Flags [%s], length %u",
                       tok2str(llc_cmd_values, "%02x", LLC_U_CMD(control)),
                       tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_U_POLL)),
                       length + hdrlen);

		if ((control & ~LLC_U_POLL) == LLC_XID) {
			if (length == 0) {
				/*
				 * XID with no payload.
				 * This could, for example, be an SNA
				 * "short form" XID.
                                 */
				return (hdrlen);
			}
			if (caplen < 1) {
				ND_PRINT("[|llc]");
				if (caplen > 0)
					ND_DEFAULTPRINT((const u_char *)p, caplen);
				return (hdrlen);
			}
			if (EXTRACT_U_1(p) == LLC_XID_FI) {
				if (caplen < 3 || length < 3) {
					ND_PRINT("[|llc]");
					if (caplen > 0)
						ND_DEFAULTPRINT((const u_char *)p, caplen);
				} else
					ND_PRINT(": %02x %02x",
						  EXTRACT_U_1(p + 1),
						  EXTRACT_U_1(p + 2));
				return (hdrlen);
			}
		}
	} else {
		if ((control & LLC_S_FMT) == LLC_S_FMT) {
			ND_PRINT("Supervisory, %s, rcv seq %u, Flags [%s], length %u",
				tok2str(llc_supervisory_values,"?",LLC_S_CMD(control)),
				LLC_IS_NR(control),
				tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
                                length + hdrlen);
			return (hdrlen);	/* no payload to print */
		} else {
			ND_PRINT("Information, send seq %u, rcv seq %u, Flags [%s], length %u",
				LLC_I_NS(control),
				LLC_IS_NR(control),
				tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
                                length + hdrlen);
		}
	}
	return (-hdrlen);
}
示例#4
0
/*
 * Returns non-zero IFF it succeeds in printing the header
 */
int
llc_print(const u_char *p, u_int length, u_int caplen,
	  const u_char *esrc, const u_char *edst)
{
	struct llc llc;
	register u_short et;
	register int ret;

	if (caplen < 3) {
		(void)printf("[|llc]");
		default_print((u_char *)p, caplen);
		return(0);
	}

	/* Watch out for possible alignment problems */
	memcpy((char *)&llc, (char *)p, min(caplen, sizeof(llc)));

	if (llc.ssap == LLCSAP_GLOBAL && llc.dsap == LLCSAP_GLOBAL) {
		ipx_print(p, length);
		return (1);
	}
#ifdef notyet
	else if (p[0] == 0xf0 && p[1] == 0xf0)
		netbios_print(p, length);
#endif
	if (llc.ssap == LLCSAP_ISONS && llc.dsap == LLCSAP_ISONS
	    && llc.llcui == LLC_UI) {
		isoclns_print(p + 3, length - 3, caplen - 3, esrc, edst);
		return (1);
	}

	if (llc.ssap == LLCSAP_SNAP && llc.dsap == LLCSAP_SNAP
	    && llc.llcui == LLC_UI) {
		if (caplen < sizeof(llc)) {
		    (void)printf("[|llc-snap]");
		    default_print((u_char *)p, caplen);
		    return (0);
		}
		if (vflag)
			(void)printf("snap %s ", protoid_string(llc.llcpi));

		caplen -= sizeof(llc);
		length -= sizeof(llc);
		p += sizeof(llc);

		/* This is an encapsulated Ethernet packet */
		et = EXTRACT_16BITS(&llc.ethertype[0]);
		ret = ether_encap_print(et, p, length, caplen);
		if (ret)
			return (ret);
	}

	if ((llc.ssap & ~LLC_GSAP) == llc.dsap) {
		if (eflag)
			(void)printf("%s ", llcsap_string(llc.dsap));
		else
			(void)printf("%s > %s %s ",
					etheraddr_string(esrc),
					etheraddr_string(edst),
					llcsap_string(llc.dsap));
	} else {
		if (eflag)
			(void)printf("%s > %s ",
				llcsap_string(llc.ssap & ~LLC_GSAP),
				llcsap_string(llc.dsap));
		else
			(void)printf("%s %s > %s %s ",
				etheraddr_string(esrc),
				llcsap_string(llc.ssap & ~LLC_GSAP),
				etheraddr_string(edst),
				llcsap_string(llc.dsap));
	}

	if ((llc.llcu & LLC_U_FMT) == LLC_U_FMT) {
		const char *m;
		char f;
		m = tok2str(cmd2str, "%02x", LLC_U_CMD(llc.llcu));
		switch ((llc.ssap & LLC_GSAP) | (llc.llcu & LLC_U_POLL)) {
		    case 0:			f = 'C'; break;
		    case LLC_GSAP:		f = 'R'; break;
		    case LLC_U_POLL:		f = 'P'; break;
		    case LLC_GSAP|LLC_U_POLL:	f = 'F'; break;
		    default:			f = '?'; break;
		}

		printf("%s/%c", m, f);

		p += 3;
		length -= 3;
		caplen -= 3;

		if ((llc.llcu & ~LLC_U_POLL) == LLC_XID) {
		    if (*p == LLC_XID_FI) {
			printf(": %02x %02x", p[1], p[2]);
			p += 3;
			length -= 3;
			caplen -= 3;
		    }
		}
	} else {
		char f;
		llc.llcis = ntohs(llc.llcis);
		switch ((llc.ssap & LLC_GSAP) | (llc.llcu & LLC_U_POLL)) {
		    case 0:			f = 'C'; break;
		    case LLC_GSAP:		f = 'R'; break;
		    case LLC_U_POLL:		f = 'P'; break;
		    case LLC_GSAP|LLC_U_POLL:	f = 'F'; break;
		    default:			f = '?'; break;
		}

		if ((llc.llcu & LLC_S_FMT) == LLC_S_FMT) {
			static char *llc_s[] = { "rr", "rej", "rnr", "03" };
			(void)printf("%s (r=%d,%c)",
				llc_s[LLC_S_CMD(llc.llcis)],
				LLC_IS_NR(llc.llcis),
				f);
		} else {
			(void)printf("I (s=%d,r=%d,%c)",
				LLC_I_NS(llc.llcis),
				LLC_IS_NR(llc.llcis),
				f);
		}
		p += 4;
		length -= 4;
		caplen -= 4;
	}
	(void)printf(" len=%d", length);
	if (caplen > 0) {
		default_print_unaligned(p, caplen);
	}
	return(1);
}