1 /*
2  * Copyright (C) 2001 WIDE Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 3. Neither the name of the project nor the names of its contributors
13  *    may be used to endorse or promote products derived from this software
14  *    without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: print-mpls.c,v 1.10 2024/09/02 16:15:32 christos Exp $");
32 #endif
33 
34 /* \summary: Multi-Protocol Label Switching (MPLS) printer */
35 
36 #include <config.h>
37 
38 #include "netdissect-stdinc.h"
39 
40 #define ND_LONGJMP_FROM_TCHECK
41 #include "netdissect.h"
42 #include "extract.h"
43 #include "mpls.h"
44 
45 static const char *mpls_labelname[] = {
46 /*0*/     "IPv4 explicit NULL", "router alert", "IPv6 explicit NULL",
47           "implicit NULL", "rsvd",
48 /*5*/     "rsvd", "rsvd", "rsvd", "rsvd", "rsvd",
49 /*10*/    "rsvd", "rsvd", "rsvd", "rsvd", "rsvd",
50 /*15*/    "rsvd",
51 };
52 
53 enum mpls_packet_type {
54           PT_UNKNOWN,
55           PT_IPV4,
56           PT_IPV6,
57           PT_OSI
58 };
59 
60 /*
61  * RFC3032: MPLS label stack encoding
62  */
63 void
mpls_print(netdissect_options * ndo,const u_char * bp,u_int length)64 mpls_print(netdissect_options *ndo, const u_char *bp, u_int length)
65 {
66           const u_char *p;
67           uint32_t label_entry;
68           uint16_t label_stack_depth = 0;
69           uint8_t first;
70           enum mpls_packet_type pt = PT_UNKNOWN;
71 
72           ndo->ndo_protocol = "mpls";
73           p = bp;
74           nd_print_protocol_caps(ndo);
75           do {
76                     if (length < sizeof(label_entry))
77                               goto invalid;
78                     label_entry = GET_BE_U_4(p);
79                     ND_PRINT("%s(label %u",
80                            (label_stack_depth && ndo->ndo_vflag) ? "\n\t" : " ",
81                               MPLS_LABEL(label_entry));
82                     label_stack_depth++;
83                     if (ndo->ndo_vflag &&
84                         MPLS_LABEL(label_entry) < sizeof(mpls_labelname) / sizeof(mpls_labelname[0]))
85                               ND_PRINT(" (%s)", mpls_labelname[MPLS_LABEL(label_entry)]);
86                     ND_PRINT(", tc %u", MPLS_TC(label_entry));
87                     if (MPLS_STACK(label_entry))
88                               ND_PRINT(", [S]");
89                     ND_PRINT(", ttl %u)", MPLS_TTL(label_entry));
90 
91                     p += sizeof(label_entry);
92                     length -= sizeof(label_entry);
93           } while (!MPLS_STACK(label_entry));
94 
95           /*
96            * Try to figure out the packet type.
97            */
98           switch (MPLS_LABEL(label_entry)) {
99 
100           case 0:   /* IPv4 explicit NULL label */
101           case 3:   /* IPv4 implicit NULL label */
102                     pt = PT_IPV4;
103                     break;
104 
105           case 2:   /* IPv6 explicit NULL label */
106                     pt = PT_IPV6;
107                     break;
108 
109           default:
110                     /*
111                      * Generally there's no indication of protocol in MPLS label
112                      * encoding.
113                      *
114                      * However, draft-hsmit-isis-aal5mux-00.txt describes a
115                      * technique for encapsulating IS-IS and IP traffic on the
116                      * same ATM virtual circuit; you look at the first payload
117                      * byte to determine the network layer protocol, based on
118                      * the fact that
119                      *
120                      *        1) the first byte of an IP header is 0x45-0x4f
121                      *           for IPv4 and 0x60-0x6f for IPv6;
122                      *
123                      *        2) the first byte of an OSI CLNP packet is 0x81,
124                      *           the first byte of an OSI ES-IS packet is 0x82,
125                      *           and the first byte of an OSI IS-IS packet is
126                      *           0x83;
127                      *
128                      * so the network layer protocol can be inferred from the
129                      * first byte of the packet, if the protocol is one of the
130                      * ones listed above.
131                      *
132                      * Cisco sends control-plane traffic MPLS-encapsulated in
133                      * this fashion.
134                      */
135                     if (length < 1) {
136                               /* nothing to print */
137                               return;
138                     }
139                     first = GET_U_1(p);
140                     pt =
141                               (first >= 0x45 && first <= 0x4f) ? PT_IPV4 :
142                               (first >= 0x60 && first <= 0x6f) ? PT_IPV6 :
143                               (first >= 0x81 && first <= 0x83) ? PT_OSI :
144                               /* ok bail out - we did not figure out what it is*/
145                               PT_UNKNOWN;
146           }
147 
148           /*
149            * Print the payload.
150            */
151           switch (pt) {
152           case PT_UNKNOWN:
153                     if (!ndo->ndo_suppress_default_print)
154                               ND_DEFAULTPRINT(p, length);
155                     break;
156 
157           case PT_IPV4:
158                     ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
159                     ip_print(ndo, p, length);
160                     break;
161 
162           case PT_IPV6:
163                     ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
164                     ip6_print(ndo, p, length);
165                     break;
166 
167           case PT_OSI:
168                     ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
169                     isoclns_print(ndo, p, length);
170                     break;
171           }
172           return;
173 
174 invalid:
175           nd_print_invalid(ndo);
176           ND_TCHECK_LEN(p, length);
177 }
178