10f74e101Schristos /* 20f74e101Schristos * Copyright (C) 2002 WIDE Project. 30f74e101Schristos * All rights reserved. 40f74e101Schristos * 50f74e101Schristos * Redistribution and use in source and binary forms, with or without 60f74e101Schristos * modification, are permitted provided that the following conditions 70f74e101Schristos * are met: 80f74e101Schristos * 1. Redistributions of source code must retain the above copyright 90f74e101Schristos * notice, this list of conditions and the following disclaimer. 100f74e101Schristos * 2. Redistributions in binary form must reproduce the above copyright 110f74e101Schristos * notice, this list of conditions and the following disclaimer in the 120f74e101Schristos * documentation and/or other materials provided with the distribution. 130f74e101Schristos * 3. Neither the name of the project nor the names of its contributors 140f74e101Schristos * may be used to endorse or promote products derived from this software 150f74e101Schristos * without specific prior written permission. 160f74e101Schristos * 170f74e101Schristos * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 180f74e101Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 190f74e101Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 200f74e101Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 210f74e101Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 220f74e101Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 230f74e101Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 240f74e101Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 250f74e101Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 260f74e101Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 270f74e101Schristos * SUCH DAMAGE. 280f74e101Schristos */ 290f74e101Schristos 3011b3aaa1Schristos #include <sys/cdefs.h> 310f74e101Schristos #ifndef lint 32*26ba0b50Schristos __RCSID("$NetBSD: print-mobility.c,v 1.10 2024/09/02 16:15:32 christos Exp $"); 3311b3aaa1Schristos #endif 34b3a00663Schristos 35dc860a36Sspz /* \summary: IPv6 mobility printer */ 3672c96ff3Schristos /* RFC 3775 */ 37dc860a36Sspz 38c74ad251Schristos #include <config.h> 390f74e101Schristos 40c74ad251Schristos #include "netdissect-stdinc.h" 410f74e101Schristos 42fdccd7e4Schristos #include "netdissect.h" 430f74e101Schristos #include "addrtoname.h" 44fdccd7e4Schristos #include "extract.h" 450f74e101Schristos 4672c96ff3Schristos #include "ip6.h" 4772c96ff3Schristos 48dc860a36Sspz 490f74e101Schristos /* Mobility header */ 500f74e101Schristos struct ip6_mobility { 51c74ad251Schristos nd_uint8_t ip6m_pproto; /* following payload protocol (for PG) */ 52c74ad251Schristos nd_uint8_t ip6m_len; /* length in units of 8 octets */ 53c74ad251Schristos nd_uint8_t ip6m_type; /* message type */ 54c74ad251Schristos nd_uint8_t reserved; /* reserved */ 55c74ad251Schristos nd_uint16_t ip6m_cksum; /* sum of IPv6 pseudo-header and MH */ 560f74e101Schristos union { 57c74ad251Schristos nd_uint16_t ip6m_un_data16[1]; /* type-specific field */ 58c74ad251Schristos nd_uint8_t ip6m_un_data8[2]; /* type-specific field */ 590f74e101Schristos } ip6m_dataun; 600f74e101Schristos }; 610f74e101Schristos 620f74e101Schristos #define ip6m_data16 ip6m_dataun.ip6m_un_data16 630f74e101Schristos #define ip6m_data8 ip6m_dataun.ip6m_un_data8 640f74e101Schristos 650f74e101Schristos #define IP6M_MINLEN 8 660f74e101Schristos 67c74ad251Schristos /* https://www.iana.org/assignments/mobility-parameters/mobility-parameters.xhtml */ 68b3a00663Schristos 690f74e101Schristos /* message type */ 700f74e101Schristos #define IP6M_BINDING_REQUEST 0 /* Binding Refresh Request */ 710f74e101Schristos #define IP6M_HOME_TEST_INIT 1 /* Home Test Init */ 720f74e101Schristos #define IP6M_CAREOF_TEST_INIT 2 /* Care-of Test Init */ 730f74e101Schristos #define IP6M_HOME_TEST 3 /* Home Test */ 740f74e101Schristos #define IP6M_CAREOF_TEST 4 /* Care-of Test */ 750f74e101Schristos #define IP6M_BINDING_UPDATE 5 /* Binding Update */ 760f74e101Schristos #define IP6M_BINDING_ACK 6 /* Binding Acknowledgement */ 770f74e101Schristos #define IP6M_BINDING_ERROR 7 /* Binding Error */ 78ba2ff121Schristos #define IP6M_MAX 7 79ba2ff121Schristos 80fdccd7e4Schristos static const struct tok ip6m_str[] = { 81fdccd7e4Schristos { IP6M_BINDING_REQUEST, "BRR" }, 82fdccd7e4Schristos { IP6M_HOME_TEST_INIT, "HoTI" }, 83fdccd7e4Schristos { IP6M_CAREOF_TEST_INIT, "CoTI" }, 84fdccd7e4Schristos { IP6M_HOME_TEST, "HoT" }, 85fdccd7e4Schristos { IP6M_CAREOF_TEST, "CoT" }, 86fdccd7e4Schristos { IP6M_BINDING_UPDATE, "BU" }, 87fdccd7e4Schristos { IP6M_BINDING_ACK, "BA" }, 88fdccd7e4Schristos { IP6M_BINDING_ERROR, "BE" }, 89fdccd7e4Schristos { 0, NULL } 90fdccd7e4Schristos }; 91fdccd7e4Schristos 92ba2ff121Schristos static const unsigned ip6m_hdrlen[IP6M_MAX + 1] = { 93ba2ff121Schristos IP6M_MINLEN, /* IP6M_BINDING_REQUEST */ 94ba2ff121Schristos IP6M_MINLEN + 8, /* IP6M_HOME_TEST_INIT */ 95ba2ff121Schristos IP6M_MINLEN + 8, /* IP6M_CAREOF_TEST_INIT */ 96ba2ff121Schristos IP6M_MINLEN + 16, /* IP6M_HOME_TEST */ 97ba2ff121Schristos IP6M_MINLEN + 16, /* IP6M_CAREOF_TEST */ 98ba2ff121Schristos IP6M_MINLEN + 4, /* IP6M_BINDING_UPDATE */ 99ba2ff121Schristos IP6M_MINLEN + 4, /* IP6M_BINDING_ACK */ 100ba2ff121Schristos IP6M_MINLEN + 16, /* IP6M_BINDING_ERROR */ 101ba2ff121Schristos }; 1020f74e101Schristos 1030f74e101Schristos /* Mobility Header Options */ 1040f74e101Schristos #define IP6MOPT_MINLEN 2 1050f74e101Schristos #define IP6MOPT_PAD1 0x0 /* Pad1 */ 1060f74e101Schristos #define IP6MOPT_PADN 0x1 /* PadN */ 1070f74e101Schristos #define IP6MOPT_REFRESH 0x2 /* Binding Refresh Advice */ 1080f74e101Schristos #define IP6MOPT_REFRESH_MINLEN 4 1090f74e101Schristos #define IP6MOPT_ALTCOA 0x3 /* Alternate Care-of Address */ 1100f74e101Schristos #define IP6MOPT_ALTCOA_MINLEN 18 1110f74e101Schristos #define IP6MOPT_NONCEID 0x4 /* Nonce Indices */ 1120f74e101Schristos #define IP6MOPT_NONCEID_MINLEN 6 1130f74e101Schristos #define IP6MOPT_AUTH 0x5 /* Binding Authorization Data */ 1140f74e101Schristos #define IP6MOPT_AUTH_MINLEN 12 1150f74e101Schristos 116c74ad251Schristos static const struct tok ip6m_binding_update_bits [] = { 117c74ad251Schristos { 0x08, "A" }, 118c74ad251Schristos { 0x04, "H" }, 119c74ad251Schristos { 0x02, "L" }, 120c74ad251Schristos { 0x01, "K" }, 121c74ad251Schristos { 0, NULL } 122c74ad251Schristos }; 123c74ad251Schristos 124dc860a36Sspz static int 125b3a00663Schristos mobility_opt_print(netdissect_options *ndo, 126b3a00663Schristos const u_char *bp, const unsigned len) 1270f74e101Schristos { 128b3a00663Schristos unsigned i, optlen; 1290f74e101Schristos 1300f74e101Schristos for (i = 0; i < len; i += optlen) { 131c74ad251Schristos if (GET_U_1(bp + i) == IP6MOPT_PAD1) 1320f74e101Schristos optlen = 1; 1330f74e101Schristos else { 134ba2ff121Schristos if (i + 1 < len) { 135c74ad251Schristos optlen = GET_U_1(bp + i + 1) + 2; 136*26ba0b50Schristos } else 1370f74e101Schristos goto trunc; 1380f74e101Schristos } 1390f74e101Schristos if (i + optlen > len) 1400f74e101Schristos goto trunc; 141c74ad251Schristos ND_TCHECK_1(bp + i + optlen); 1420f74e101Schristos 143c74ad251Schristos switch (GET_U_1(bp + i)) { 1440f74e101Schristos case IP6MOPT_PAD1: 145c74ad251Schristos ND_PRINT("(pad1)"); 1460f74e101Schristos break; 1470f74e101Schristos case IP6MOPT_PADN: 1480f74e101Schristos if (len - i < IP6MOPT_MINLEN) { 149c74ad251Schristos ND_PRINT("(padn: trunc)"); 1500f74e101Schristos goto trunc; 1510f74e101Schristos } 152c74ad251Schristos ND_PRINT("(padn)"); 1530f74e101Schristos break; 1540f74e101Schristos case IP6MOPT_REFRESH: 1550f74e101Schristos if (len - i < IP6MOPT_REFRESH_MINLEN) { 156c74ad251Schristos ND_PRINT("(refresh: trunc)"); 1570f74e101Schristos goto trunc; 1580f74e101Schristos } 1590f74e101Schristos /* units of 4 secs */ 160c74ad251Schristos ND_PRINT("(refresh: %u)", 161c74ad251Schristos GET_BE_U_2(bp + i + 2) << 2); 1620f74e101Schristos break; 1630f74e101Schristos case IP6MOPT_ALTCOA: 1640f74e101Schristos if (len - i < IP6MOPT_ALTCOA_MINLEN) { 165c74ad251Schristos ND_PRINT("(altcoa: trunc)"); 1660f74e101Schristos goto trunc; 1670f74e101Schristos } 168c74ad251Schristos ND_PRINT("(alt-CoA: %s)", GET_IP6ADDR_STRING(bp + i + 2)); 1690f74e101Schristos break; 1700f74e101Schristos case IP6MOPT_NONCEID: 1710f74e101Schristos if (len - i < IP6MOPT_NONCEID_MINLEN) { 172c74ad251Schristos ND_PRINT("(ni: trunc)"); 1730f74e101Schristos goto trunc; 1740f74e101Schristos } 175c74ad251Schristos ND_PRINT("(ni: ho=0x%04x co=0x%04x)", 176c74ad251Schristos GET_BE_U_2(bp + i + 2), 177c74ad251Schristos GET_BE_U_2(bp + i + 4)); 1780f74e101Schristos break; 1790f74e101Schristos case IP6MOPT_AUTH: 1800f74e101Schristos if (len - i < IP6MOPT_AUTH_MINLEN) { 181c74ad251Schristos ND_PRINT("(auth: trunc)"); 1820f74e101Schristos goto trunc; 1830f74e101Schristos } 184c74ad251Schristos ND_PRINT("(auth)"); 1850f74e101Schristos break; 1860f74e101Schristos default: 1870f74e101Schristos if (len - i < IP6MOPT_MINLEN) { 188c74ad251Schristos ND_PRINT("(sopt_type %u: trunc)", 189c74ad251Schristos GET_U_1(bp + i)); 1900f74e101Schristos goto trunc; 1910f74e101Schristos } 192c74ad251Schristos ND_PRINT("(type-0x%02x: len=%u)", GET_U_1(bp + i), 193c74ad251Schristos GET_U_1(bp + i + 1)); 1940f74e101Schristos break; 1950f74e101Schristos } 1960f74e101Schristos } 197dc860a36Sspz return 0; 1980f74e101Schristos 1990f74e101Schristos trunc: 200dc860a36Sspz return 1; 2010f74e101Schristos } 2020f74e101Schristos 2030f74e101Schristos /* 2040f74e101Schristos * Mobility Header 2050f74e101Schristos */ 2060f74e101Schristos int 207b3a00663Schristos mobility_print(netdissect_options *ndo, 208b3a00663Schristos const u_char *bp, const u_char *bp2 _U_) 2090f74e101Schristos { 2100f74e101Schristos const struct ip6_mobility *mh; 2110f74e101Schristos const u_char *ep; 212b3a00663Schristos unsigned mhlen, hlen; 213b3a00663Schristos uint8_t type; 2140f74e101Schristos 215c74ad251Schristos ndo->ndo_protocol = "mobility"; 216fdccd7e4Schristos mh = (const struct ip6_mobility *)bp; 2170f74e101Schristos 2180f74e101Schristos /* 'ep' points to the end of available data. */ 219b3a00663Schristos ep = ndo->ndo_snapend; 2200f74e101Schristos 221c74ad251Schristos if (!ND_TTEST_1(mh->ip6m_len)) { 2220f74e101Schristos /* 2230f74e101Schristos * There's not enough captured data to include the 2240f74e101Schristos * mobility header length. 2250f74e101Schristos * 2260f74e101Schristos * Our caller expects us to return the length, however, 2270f74e101Schristos * so return a value that will run to the end of the 2280f74e101Schristos * captured data. 2290f74e101Schristos * 2300f74e101Schristos * XXX - "ip6_print()" doesn't do anything with the 2310f74e101Schristos * returned length, however, as it breaks out of the 2320f74e101Schristos * header-processing loop. 2330f74e101Schristos */ 234c74ad251Schristos mhlen = (unsigned)(ep - bp); 2350f74e101Schristos goto trunc; 2360f74e101Schristos } 237c74ad251Schristos mhlen = (GET_U_1(mh->ip6m_len) + 1) << 3; 2380f74e101Schristos 2390f74e101Schristos /* XXX ip6m_cksum */ 2400f74e101Schristos 241c74ad251Schristos type = GET_U_1(mh->ip6m_type); 242ba2ff121Schristos if (type <= IP6M_MAX && mhlen < ip6m_hdrlen[type]) { 243c74ad251Schristos ND_PRINT("(header length %u is too small for type %u)", mhlen, type); 244ba2ff121Schristos goto trunc; 245ba2ff121Schristos } 246c74ad251Schristos ND_PRINT("mobility: %s", tok2str(ip6m_str, "type-#%u", type)); 2470f74e101Schristos switch (type) { 2480f74e101Schristos case IP6M_BINDING_REQUEST: 2490f74e101Schristos hlen = IP6M_MINLEN; 2500f74e101Schristos break; 2510f74e101Schristos case IP6M_HOME_TEST_INIT: 2520f74e101Schristos case IP6M_CAREOF_TEST_INIT: 2530f74e101Schristos hlen = IP6M_MINLEN; 254b3a00663Schristos if (ndo->ndo_vflag) { 255c74ad251Schristos ND_PRINT(" %s Init Cookie=%08x:%08x", 2560f74e101Schristos type == IP6M_HOME_TEST_INIT ? "Home" : "Care-of", 257c74ad251Schristos GET_BE_U_4(bp + hlen), 258c74ad251Schristos GET_BE_U_4(bp + hlen + 4)); 2590f74e101Schristos } 2600f74e101Schristos hlen += 8; 2610f74e101Schristos break; 2620f74e101Schristos case IP6M_HOME_TEST: 2630f74e101Schristos case IP6M_CAREOF_TEST: 264c74ad251Schristos ND_PRINT(" nonce id=0x%x", GET_BE_U_2(mh->ip6m_data16[0])); 2650f74e101Schristos hlen = IP6M_MINLEN; 266b3a00663Schristos if (ndo->ndo_vflag) { 267c74ad251Schristos ND_PRINT(" %s Init Cookie=%08x:%08x", 2680f74e101Schristos type == IP6M_HOME_TEST ? "Home" : "Care-of", 269c74ad251Schristos GET_BE_U_4(bp + hlen), 270c74ad251Schristos GET_BE_U_4(bp + hlen + 4)); 2710f74e101Schristos } 2720f74e101Schristos hlen += 8; 273b3a00663Schristos if (ndo->ndo_vflag) { 274c74ad251Schristos ND_PRINT(" %s Keygen Token=%08x:%08x", 2750f74e101Schristos type == IP6M_HOME_TEST ? "Home" : "Care-of", 276c74ad251Schristos GET_BE_U_4(bp + hlen), 277c74ad251Schristos GET_BE_U_4(bp + hlen + 4)); 2780f74e101Schristos } 2790f74e101Schristos hlen += 8; 2800f74e101Schristos break; 2810f74e101Schristos case IP6M_BINDING_UPDATE: 282c74ad251Schristos { 283c74ad251Schristos int bits; 284c74ad251Schristos ND_PRINT(" seq#=%u", GET_BE_U_2(mh->ip6m_data16[0])); 2850f74e101Schristos hlen = IP6M_MINLEN; 286c74ad251Schristos ND_TCHECK_2(bp + hlen); 287c74ad251Schristos bits = (GET_U_1(bp + hlen) & 0xf0) >> 4; 288c74ad251Schristos if (bits) { 289c74ad251Schristos ND_PRINT(" "); 290c74ad251Schristos ND_PRINT("%s", 291c74ad251Schristos bittok2str_nosep(ip6m_binding_update_bits, 292c74ad251Schristos "bits-#0x%x", bits)); 29372c96ff3Schristos } 2940f74e101Schristos /* Reserved (4bits) */ 2950f74e101Schristos hlen += 1; 2960f74e101Schristos /* Reserved (8bits) */ 2970f74e101Schristos hlen += 1; 2980f74e101Schristos /* units of 4 secs */ 299c74ad251Schristos ND_PRINT(" lifetime=%u", GET_BE_U_2(bp + hlen) << 2); 3000f74e101Schristos hlen += 2; 3010f74e101Schristos break; 302c74ad251Schristos } 3030f74e101Schristos case IP6M_BINDING_ACK: 304c74ad251Schristos ND_PRINT(" status=%u", GET_U_1(mh->ip6m_data8[0])); 305c74ad251Schristos if (GET_U_1(mh->ip6m_data8[1]) & 0x80) 306c74ad251Schristos ND_PRINT(" K"); 3070f74e101Schristos /* Reserved (7bits) */ 3080f74e101Schristos hlen = IP6M_MINLEN; 309c74ad251Schristos ND_PRINT(" seq#=%u", GET_BE_U_2(bp + hlen)); 3100f74e101Schristos hlen += 2; 3110f74e101Schristos /* units of 4 secs */ 312c74ad251Schristos ND_PRINT(" lifetime=%u", GET_BE_U_2(bp + hlen) << 2); 3130f74e101Schristos hlen += 2; 3140f74e101Schristos break; 3150f74e101Schristos case IP6M_BINDING_ERROR: 316c74ad251Schristos ND_PRINT(" status=%u", GET_U_1(mh->ip6m_data8[0])); 3170f74e101Schristos /* Reserved */ 3180f74e101Schristos hlen = IP6M_MINLEN; 319c74ad251Schristos ND_PRINT(" homeaddr %s", GET_IP6ADDR_STRING(bp + hlen)); 3200f74e101Schristos hlen += 16; 3210f74e101Schristos break; 3220f74e101Schristos default: 323c74ad251Schristos ND_PRINT(" len=%u", GET_U_1(mh->ip6m_len)); 3240f74e101Schristos return(mhlen); 3250f74e101Schristos break; 3260f74e101Schristos } 327b3a00663Schristos if (ndo->ndo_vflag) 328c74ad251Schristos if (mobility_opt_print(ndo, bp + hlen, mhlen - hlen)) 329c74ad251Schristos goto trunc; 3300f74e101Schristos 3310f74e101Schristos return(mhlen); 3320f74e101Schristos 3330f74e101Schristos trunc: 334c74ad251Schristos nd_print_trunc(ndo); 33572c96ff3Schristos return(-1); 3360f74e101Schristos } 337