1 /*
2  * Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Lawrence Berkeley Laboratory,
11  * Berkeley, CA.  The name of the University may not be used to
12  * endorse or promote products derived from this software without
13  * specific prior written permission.
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17  *
18  * Initial contribution from Jeff Honig (jch@MITCHELL.CIT.CORNELL.EDU).
19  */
20 
21 #include <sys/cdefs.h>
22 #ifndef lint
23 __RCSID("$NetBSD: print-egp.c,v 1.8 2024/09/02 16:15:31 christos Exp $");
24 #endif
25 
26 /* \summary: Exterior Gateway Protocol (EGP) printer */
27 
28 /* specification: RFC 827 */
29 
30 #include <config.h>
31 
32 #include "netdissect-stdinc.h"
33 
34 #include "netdissect.h"
35 #include "addrtoname.h"
36 #include "extract.h"
37 
38 struct egp_packet {
39           nd_uint8_t  egp_version;
40 #define   EGP_VERSION         2
41           nd_uint8_t  egp_type;
42 #define  EGPT_ACQUIRE         3
43 #define  EGPT_REACH 5
44 #define  EGPT_POLL  2
45 #define  EGPT_UPDATE          1
46 #define  EGPT_ERROR 8
47           nd_uint8_t  egp_code;
48 #define  EGPC_REQUEST         0
49 #define  EGPC_CONFIRM         1
50 #define  EGPC_REFUSE          2
51 #define  EGPC_CEASE 3
52 #define  EGPC_CEASEACK        4
53 #define  EGPC_HELLO 0
54 #define  EGPC_HEARDU          1
55           nd_uint8_t  egp_status;
56 #define  EGPS_UNSPEC          0
57 #define  EGPS_ACTIVE          1
58 #define  EGPS_PASSIVE         2
59 #define  EGPS_NORES 3
60 #define  EGPS_ADMIN 4
61 #define  EGPS_GODOWN          5
62 #define  EGPS_PARAM 6
63 #define  EGPS_PROTO 7
64 #define  EGPS_INDET 0
65 #define  EGPS_UP    1
66 #define  EGPS_DOWN  2
67 #define  EGPS_UNSOL 0x80
68           nd_uint16_t  egp_checksum;
69           nd_uint16_t  egp_as;
70           nd_uint16_t  egp_sequence;
71           union {
72                     nd_uint16_t egpu_hello;
73                     nd_uint8_t  egpu_gws[2];
74                     nd_uint16_t egpu_reason;
75 #define  EGPR_UNSPEC          0
76 #define  EGPR_BADHEAD         1
77 #define  EGPR_BADDATA         2
78 #define  EGPR_NOREACH         3
79 #define  EGPR_XSPOLL          4
80 #define  EGPR_NORESP          5
81 #define  EGPR_UVERSION        6
82           } egp_handg;
83 #define  egp_hello  egp_handg.egpu_hello
84 #define  egp_intgw  egp_handg.egpu_gws[0]
85 #define  egp_extgw  egp_handg.egpu_gws[1]
86 #define  egp_reason  egp_handg.egpu_reason
87           union {
88                     nd_uint16_t egpu_poll;
89                     nd_ipv4 egpu_sourcenet;
90           } egp_pands;
91 #define  egp_poll  egp_pands.egpu_poll
92 #define  egp_sourcenet  egp_pands.egpu_sourcenet
93 };
94 
95 static const char *egp_acquire_codes[] = {
96           "request",
97           "confirm",
98           "refuse",
99           "cease",
100           "cease_ack"
101 };
102 
103 static const char *egp_acquire_status[] = {
104           "unspecified",
105           "active_mode",
106           "passive_mode",
107           "insufficient_resources",
108           "administratively_prohibited",
109           "going_down",
110           "parameter_violation",
111           "protocol_violation"
112 };
113 
114 static const char *egp_reach_codes[] = {
115           "hello",
116           "i-h-u"
117 };
118 
119 static const char *egp_status_updown[] = {
120           "indeterminate",
121           "up",
122           "down"
123 };
124 
125 static const char *egp_reasons[] = {
126           "unspecified",
127           "bad_EGP_header_format",
128           "bad_EGP_data_field_format",
129           "reachability_info_unavailable",
130           "excessive_polling_rate",
131           "no_response",
132           "unsupported_version"
133 };
134 
135 static void
egpnr_print(netdissect_options * ndo,const struct egp_packet * egp,u_int length)136 egpnr_print(netdissect_options *ndo,
137            const struct egp_packet *egp, u_int length)
138 {
139           const uint8_t *cp;
140           uint32_t addr;
141           uint32_t net;
142           u_int netlen;
143           u_int gateways, distances, networks;
144           u_int intgw, extgw, t_gateways;
145           const char *comma;
146 
147           addr = GET_IPV4_TO_NETWORK_ORDER(egp->egp_sourcenet);
148           if (IN_CLASSA(addr)) {
149                     net = addr & IN_CLASSA_NET;
150                     netlen = 1;
151           } else if (IN_CLASSB(addr)) {
152                     net = addr & IN_CLASSB_NET;
153                     netlen = 2;
154           } else if (IN_CLASSC(addr)) {
155                     net = addr & IN_CLASSC_NET;
156                     netlen = 3;
157           } else {
158                     net = 0;
159                     netlen = 0;
160           }
161           cp = (const uint8_t *)(egp + 1);
162           length -= sizeof(*egp);
163 
164           intgw = GET_U_1(egp->egp_intgw);
165           extgw = GET_U_1(egp->egp_extgw);
166           t_gateways = intgw + extgw;
167           for (gateways = 0; gateways < t_gateways; ++gateways) {
168                     /* Pickup host part of gateway address */
169                     addr = 0;
170                     if (length < 4 - netlen)
171                               goto trunc;
172                     ND_TCHECK_LEN(cp, 4 - netlen);
173                     switch (netlen) {
174 
175                     case 1:
176                               addr = GET_U_1(cp);
177                               cp++;
178                               /* fall through */
179                     case 2:
180                               addr = (addr << 8) | GET_U_1(cp);
181                               cp++;
182                               /* fall through */
183                     case 3:
184                               addr = (addr << 8) | GET_U_1(cp);
185                               cp++;
186                               break;
187                     }
188                     addr |= net;
189                     length -= 4 - netlen;
190                     if (length < 1)
191                               goto trunc;
192                     distances = GET_U_1(cp);
193                     cp++;
194                     length--;
195                     ND_PRINT(" %s %s ",
196                            gateways < intgw ? "int" : "ext",
197                            ipaddr_string(ndo, (const u_char *)&addr)); /* local buffer, not packet data; don't use GET_IPADDR_STRING() */
198 
199                     comma = "";
200                     ND_PRINT("(");
201                     while (distances != 0) {
202                               if (length < 2)
203                                         goto trunc;
204                               ND_PRINT("%sd%u:", comma, GET_U_1(cp));
205                               cp++;
206                               comma = ", ";
207                               networks = GET_U_1(cp);
208                               cp++;
209                               length -= 2;
210                               while (networks != 0) {
211                                         /* Pickup network number */
212                                         if (length < 1)
213                                                   goto trunc;
214                                         addr = ((uint32_t) GET_U_1(cp)) << 24;
215                                         cp++;
216                                         length--;
217                                         if (IN_CLASSB(addr)) {
218                                                   if (length < 1)
219                                                             goto trunc;
220                                                   addr |= ((uint32_t) GET_U_1(cp)) << 16;
221                                                   cp++;
222                                                   length--;
223                                         } else if (!IN_CLASSA(addr)) {
224                                                   if (length < 2)
225                                                             goto trunc;
226                                                   addr |= ((uint32_t) GET_U_1(cp)) << 16;
227                                                   cp++;
228                                                   addr |= ((uint32_t) GET_U_1(cp)) << 8;
229                                                   cp++;
230                                                   length -= 2;
231                                         }
232                                         ND_PRINT(" %s", ipaddr_string(ndo, (const u_char *)&addr)); /* local buffer, not packet data; don't use GET_IPADDR_STRING() */
233                                         networks--;
234                               }
235                               distances--;
236                     }
237                     ND_PRINT(")");
238           }
239           return;
240 trunc:
241           nd_print_trunc(ndo);
242 }
243 
244 void
egp_print(netdissect_options * ndo,const uint8_t * bp,u_int length)245 egp_print(netdissect_options *ndo,
246           const uint8_t *bp, u_int length)
247 {
248           const struct egp_packet *egp;
249           u_int version;
250           u_int type;
251           u_int code;
252           u_int status;
253 
254           ndo->ndo_protocol = "egp";
255           egp = (const struct egp_packet *)bp;
256           if (length < sizeof(*egp) || !ND_TTEST_SIZE(egp)) {
257                     nd_print_trunc(ndo);
258                     return;
259           }
260 
261           version = GET_U_1(egp->egp_version);
262         if (!ndo->ndo_vflag) {
263             ND_PRINT("EGPv%u, AS %u, seq %u, length %u",
264                    version,
265                    GET_BE_U_2(egp->egp_as),
266                    GET_BE_U_2(egp->egp_sequence),
267                    length);
268             return;
269         } else
270             ND_PRINT("EGPv%u, length %u",
271                    version,
272                    length);
273 
274           if (version != EGP_VERSION) {
275                     ND_PRINT("[version %u]", version);
276                     return;
277           }
278 
279           type = GET_U_1(egp->egp_type);
280           code = GET_U_1(egp->egp_code);
281           status = GET_U_1(egp->egp_status);
282 
283           switch (type) {
284           case EGPT_ACQUIRE:
285                     ND_PRINT(" acquire");
286                     switch (code) {
287                     case EGPC_REQUEST:
288                     case EGPC_CONFIRM:
289                               ND_PRINT(" %s", egp_acquire_codes[code]);
290                               switch (status) {
291                               case EGPS_UNSPEC:
292                               case EGPS_ACTIVE:
293                               case EGPS_PASSIVE:
294                                         ND_PRINT(" %s", egp_acquire_status[status]);
295                                         break;
296 
297                               default:
298                                         ND_PRINT(" [status %u]", status);
299                                         break;
300                               }
301                               ND_PRINT(" hello:%u poll:%u",
302                                      GET_BE_U_2(egp->egp_hello),
303                                      GET_BE_U_2(egp->egp_poll));
304                               break;
305 
306                     case EGPC_REFUSE:
307                     case EGPC_CEASE:
308                     case EGPC_CEASEACK:
309                               ND_PRINT(" %s", egp_acquire_codes[code]);
310                               switch (status ) {
311                               case EGPS_UNSPEC:
312                               case EGPS_NORES:
313                               case EGPS_ADMIN:
314                               case EGPS_GODOWN:
315                               case EGPS_PARAM:
316                               case EGPS_PROTO:
317                                         ND_PRINT(" %s", egp_acquire_status[status]);
318                                         break;
319 
320                               default:
321                                         ND_PRINT("[status %u]", status);
322                                         break;
323                               }
324                               break;
325 
326                     default:
327                               ND_PRINT("[code %u]", code);
328                               break;
329                     }
330                     break;
331 
332           case EGPT_REACH:
333                     switch (code) {
334 
335                     case EGPC_HELLO:
336                     case EGPC_HEARDU:
337                               ND_PRINT(" %s", egp_reach_codes[code]);
338                               if (status <= EGPS_DOWN)
339                                         ND_PRINT(" state:%s", egp_status_updown[status]);
340                               else
341                                         ND_PRINT(" [status %u]", status);
342                               break;
343 
344                     default:
345                               ND_PRINT("[reach code %u]", code);
346                               break;
347                     }
348                     break;
349 
350           case EGPT_POLL:
351                     ND_PRINT(" poll");
352                     if (status <= EGPS_DOWN)
353                               ND_PRINT(" state:%s", egp_status_updown[status]);
354                     else
355                               ND_PRINT(" [status %u]", status);
356                     ND_PRINT(" net:%s", GET_IPADDR_STRING(egp->egp_sourcenet));
357                     break;
358 
359           case EGPT_UPDATE:
360                     ND_PRINT(" update");
361                     if (status & EGPS_UNSOL) {
362                               status &= ~EGPS_UNSOL;
363                               ND_PRINT(" unsolicited");
364                     }
365                     if (status <= EGPS_DOWN)
366                               ND_PRINT(" state:%s", egp_status_updown[status]);
367                     else
368                               ND_PRINT(" [status %u]", status);
369                     ND_PRINT(" %s int %u ext %u",
370                            GET_IPADDR_STRING(egp->egp_sourcenet),
371                            GET_U_1(egp->egp_intgw),
372                            GET_U_1(egp->egp_extgw));
373                     if (ndo->ndo_vflag)
374                               egpnr_print(ndo, egp, length);
375                     break;
376 
377           case EGPT_ERROR:
378                     ND_PRINT(" error");
379                     if (status <= EGPS_DOWN)
380                               ND_PRINT(" state:%s", egp_status_updown[status]);
381                     else
382                               ND_PRINT(" [status %u]", status);
383 
384                     if (GET_BE_U_2(egp->egp_reason) <= EGPR_UVERSION)
385                               ND_PRINT(" %s",
386                                          egp_reasons[GET_BE_U_2(egp->egp_reason)]);
387                     else
388                               ND_PRINT(" [reason %u]", GET_BE_U_2(egp->egp_reason));
389                     break;
390 
391           default:
392                     ND_PRINT("[type %u]", type);
393                     break;
394           }
395 }
396