/* * Copyright (c) 1989, 1990, 1991, 1993, 1994, 1996 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* \summary: Routing Information Protocol (RIP) printer */ /* specification: RFC 1058, RFC 2453, RFC 4822 */ #ifdef HAVE_CONFIG_H #include #endif #include "netdissect-stdinc.h" #include "netdissect.h" #include "addrtoname.h" #include "extract.h" #include "af.h" /* * RFC 1058 and RFC 2453 header of packet. * * 0 1 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Command (1) | Version (1) | unused | * +---------------+---------------+-------------------------------+ */ struct rip { nd_uint8_t rip_cmd; /* request/response */ nd_uint8_t rip_vers; /* protocol version # */ nd_byte unused[2]; /* unused */ }; #define RIPCMD_REQUEST 1 /* want info */ #define RIPCMD_RESPONSE 2 /* responding to request */ #define RIPCMD_TRACEON 3 /* turn tracing on */ #define RIPCMD_TRACEOFF 4 /* turn it off */ #define RIPCMD_POLL 5 /* want info from everybody */ #define RIPCMD_POLLENTRY 6 /* poll for entry */ static const struct tok rip_cmd_values[] = { { RIPCMD_REQUEST, "Request" }, { RIPCMD_RESPONSE, "Response" }, { RIPCMD_TRACEON, "Trace on" }, { RIPCMD_TRACEOFF, "Trace off" }, { RIPCMD_POLL, "Poll" }, { RIPCMD_POLLENTRY, "Poll Entry" }, { 0, NULL} }; #define RIP_AUTHLEN 16 #define RIP_ROUTELEN 20 /* * First 4 bytes of all RIPv1/RIPv2 entries. */ struct rip_entry_header { nd_uint16_t rip_family; nd_uint16_t rip_tag; }; /* * RFC 1058 entry. * * 0 1 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Address Family Identifier (2) | must be zero (2) | * +-------------------------------+-------------------------------+ * | IP Address (4) | * +---------------------------------------------------------------+ * | must be zero (4) | * +---------------------------------------------------------------+ * | must be zero (4) | * +---------------------------------------------------------------+ * | Metric (4) | * +---------------------------------------------------------------+ */ struct rip_netinfo_v1 { nd_uint16_t rip_family; nd_byte rip_mbz1[2]; nd_ipv4 rip_dest; nd_byte rip_mbz2[4]; nd_byte rip_mbz3[4]; nd_uint32_t rip_metric; /* cost of route */ }; /* * RFC 2453 route entry * * 0 1 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Address Family Identifier (2) | Route Tag (2) | * +-------------------------------+-------------------------------+ * | IP Address (4) | * +---------------------------------------------------------------+ * | Subnet Mask (4) | * +---------------------------------------------------------------+ * | Next Hop (4) | * +---------------------------------------------------------------+ * | Metric (4) | * +---------------------------------------------------------------+ * */ struct rip_netinfo_v2 { nd_uint16_t rip_family; nd_uint16_t rip_tag; nd_ipv4 rip_dest; nd_uint32_t rip_dest_mask; nd_ipv4 rip_router; nd_uint32_t rip_metric; /* cost of route */ }; /* * RFC 2453 authentication entry * * 0 1 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0xFFFF | Authentication Type (2) | * +-------------------------------+-------------------------------+ * - Authentication (16) - * +---------------------------------------------------------------+ */ struct rip_auth_v2 { nd_uint16_t rip_family; nd_uint16_t rip_tag; nd_byte rip_auth[16]; }; /* * RFC 4822 Cryptographic Authentication entry. * * 0 1 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RIPv2 Packet Length | Key ID | Auth Data Len | * +---------------+---------------+---------------+---------------+ * | Sequence Number (non-decreasing) | * +---------------+---------------+---------------+---------------+ * | reserved must be zero | * +---------------+---------------+---------------+---------------+ * | reserved must be zero | * +---------------+---------------+---------------+---------------+ */ struct rip_auth_crypto_v2 { nd_uint16_t rip_packet_len; nd_uint8_t rip_key_id; nd_uint8_t rip_auth_data_len; nd_uint32_t rip_seq_num; nd_byte rip_mbz1[4]; nd_byte rip_mbz2[4]; }; static unsigned rip_entry_print_v1(netdissect_options *ndo, const u_char *p, unsigned remaining) { const struct rip_entry_header *eh = (const struct rip_entry_header *)p; u_short family; const struct rip_netinfo_v1 *ni = (const struct rip_netinfo_v1 *)p; /* RFC 1058 */ if (remaining < RIP_ROUTELEN) return (0); ND_TCHECK_SIZE(ni); family = GET_BE_U_2(ni->rip_family); if (family != BSD_AFNUM_INET && family != 0) { ND_PRINT("\n\t AFI %s, ", tok2str(bsd_af_values, "Unknown (%u)", family)); print_unknown_data(ndo, p + sizeof(*eh), "\n\t ", RIP_ROUTELEN - sizeof(*eh)); return (RIP_ROUTELEN); } if (GET_BE_U_2(ni->rip_mbz1) || GET_BE_U_4(ni->rip_mbz2) || GET_BE_U_4(ni->rip_mbz3)) { /* MBZ fields not zero */ print_unknown_data(ndo, p, "\n\t ", RIP_ROUTELEN); return (RIP_ROUTELEN); } if (family == 0) { ND_PRINT("\n\t AFI 0, %s, metric: %u", GET_IPADDR_STRING(ni->rip_dest), GET_BE_U_4(ni->rip_metric)); return (RIP_ROUTELEN); } /* BSD_AFNUM_INET */ ND_PRINT("\n\t %s, metric: %u", GET_IPADDR_STRING(ni->rip_dest), GET_BE_U_4(ni->rip_metric)); return (RIP_ROUTELEN); trunc: return 0; } static unsigned rip_entry_print_v2(netdissect_options *ndo, const u_char *p, unsigned remaining) { const struct rip_entry_header *eh = (const struct rip_entry_header *)p; u_short family; const struct rip_netinfo_v2 *ni; if (remaining < sizeof(*eh)) return (0); ND_TCHECK_SIZE(eh); family = GET_BE_U_2(eh->rip_family); if (family == 0xFFFF) { /* variable-sized authentication structures */ uint16_t auth_type = GET_BE_U_2(eh->rip_tag); p += sizeof(*eh); remaining -= sizeof(*eh); if (auth_type == 2) { ND_PRINT("\n\t Simple Text Authentication data: "); nd_printjnp(ndo, p, RIP_AUTHLEN); } else if (auth_type == 3) { const struct rip_auth_crypto_v2 *ch; ch = (const struct rip_auth_crypto_v2 *)p; ND_TCHECK_SIZE(ch); if (remaining < sizeof(*ch)) return (0); ND_PRINT("\n\t Auth header:"); ND_PRINT(" Packet Len %u,", GET_BE_U_2(ch->rip_packet_len)); ND_PRINT(" Key-ID %u,", GET_U_1(ch->rip_key_id)); ND_PRINT(" Auth Data Len %u,", GET_U_1(ch->rip_auth_data_len)); ND_PRINT(" SeqNo %u,", GET_BE_U_4(ch->rip_seq_num)); ND_PRINT(" MBZ %u,", GET_BE_U_4(ch->rip_mbz1)); ND_PRINT(" MBZ %u", GET_BE_U_4(ch->rip_mbz2)); } else if (auth_type == 1) { ND_PRINT("\n\t Auth trailer:"); print_unknown_data(ndo, p, "\n\t ", remaining); return (sizeof(*eh) + remaining); /* AT spans till the packet end */ } else { ND_PRINT("\n\t Unknown (%u) Authentication data:", auth_type); print_unknown_data(ndo, p, "\n\t ", remaining); return (sizeof(*eh) + remaining); /* we don't know how long this is, so we go to the packet end */ } } else if (family != BSD_AFNUM_INET && family != 0) { ND_PRINT("\n\t AFI %s", tok2str(bsd_af_values, "Unknown (%u)", family)); print_unknown_data(ndo, p + sizeof(*eh), "\n\t ", RIP_ROUTELEN - sizeof(*eh)); } else { /* BSD_AFNUM_INET or AFI 0 */ ni = (const struct rip_netinfo_v2 *)p; ND_TCHECK_SIZE(ni); if (remaining < sizeof(*ni)) return (0); ND_PRINT("\n\t AFI %s, %15s/%-2d, tag 0x%04x, metric: %u, next-hop: ", tok2str(bsd_af_values, "%u", family), GET_IPADDR_STRING(ni->rip_dest), mask2plen(GET_BE_U_4(ni->rip_dest_mask)), GET_BE_U_2(ni->rip_tag), GET_BE_U_4(ni->rip_metric)); if (GET_BE_U_4(ni->rip_router)) ND_PRINT("%s", GET_IPADDR_STRING(ni->rip_router)); else ND_PRINT("self"); } return (RIP_ROUTELEN); trunc: return 0; } void rip_print(netdissect_options *ndo, const u_char *dat, u_int length) { const struct rip *rp; uint8_t vers, cmd; const u_char *p; u_int len, routecount; unsigned entry_size; ndo->ndo_protocol = "rip"; if (ndo->ndo_snapend < dat) { nd_print_trunc(ndo); return; } len = ND_BYTES_AVAILABLE_AFTER(dat); if (len > length) len = length; if (len < sizeof(*rp)) { nd_print_trunc(ndo); return; } len -= sizeof(*rp); rp = (const struct rip *)dat; ND_TCHECK_SIZE(rp); vers = GET_U_1(rp->rip_vers); ND_PRINT("%sRIPv%u", (ndo->ndo_vflag >= 1) ? "\n\t" : "", vers); if (vers == 0) { /* * RFC 1058. * * XXX - RFC 1058 says * * 0 Datagrams whose version number is zero are to be ignored. * These are from a previous version of the protocol, whose * packet format was machine-specific. * * so perhaps we should just dump the packet, in hex. */ print_unknown_data(ndo, (const uint8_t *)&rp->rip_cmd, "\n\t", length); return; } /* dump version and lets see if we know the commands name*/ cmd = GET_U_1(rp->rip_cmd); ND_PRINT(", %s, length: %u", tok2str(rip_cmd_values, "unknown command (%u)", cmd), length); if (ndo->ndo_vflag < 1) return; switch (cmd) { case RIPCMD_REQUEST: case RIPCMD_RESPONSE: switch (vers) { case 1: routecount = length / RIP_ROUTELEN; ND_PRINT(", routes: %u", routecount); p = (const u_char *)(rp + 1); while (len != 0) { entry_size = rip_entry_print_v1(ndo, p, len); if (entry_size == 0) { /* Error */ nd_print_trunc(ndo); break; } if (len < entry_size) { ND_PRINT(" [remaining entries length %u < %u]", len, entry_size); nd_print_invalid(ndo); break; } p += entry_size; len -= entry_size; } break; case 2: routecount = length / RIP_ROUTELEN; ND_PRINT(", routes: %u or less", routecount); p = (const u_char *)(rp + 1); while (len != 0) { entry_size = rip_entry_print_v2(ndo, p, len); if (entry_size == 0) { /* Error */ nd_print_trunc(ndo); break; } if (len < entry_size) { ND_PRINT(" [remaining entries length %u < %u]", len, entry_size); nd_print_invalid(ndo); break; } p += entry_size; len -= entry_size; } break; default: ND_PRINT(", unknown version"); break; } break; case RIPCMD_TRACEOFF: case RIPCMD_POLL: case RIPCMD_POLLENTRY: break; case RIPCMD_TRACEON: /* fall through */ default: if (ndo->ndo_vflag <= 1) { if (!print_unknown_data(ndo, (const uint8_t *)rp, "\n\t", length)) return; } break; } /* do we want to see an additionally hexdump ? */ if (ndo->ndo_vflag> 1) { if (!print_unknown_data(ndo, (const uint8_t *)rp, "\n\t", length)) return; } trunc: return; }