1 /* 2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that: (1) source code distributions 7 * retain the above copyright notice and this paragraph in its entirety, (2) 8 * distributions including binary code include the above copyright notice and 9 * this paragraph in its entirety in the documentation or other materials 10 * provided with the distribution, and (3) all advertising materials mentioning 11 * features or use of this software display the following acknowledgement: 12 * ``This product includes software developed by the University of California, 13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 14 * the University nor the names of its contributors may be used to endorse 15 * or promote products derived from this software without specific prior 16 * written permission. 17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 20 * 21 * $FreeBSD$ 22 */ 23 24 #define NETDISSECT_REWORKED 25 #ifdef HAVE_CONFIG_H 26 #include "config.h" 27 #endif 28 29 #include <tcpdump-stdinc.h> 30 31 #include <string.h> 32 33 #include "interface.h" 34 #include "addrtoname.h" 35 #include "extract.h" /* must come after interface.h */ 36 37 #include "ip.h" 38 #include "ipproto.h" 39 40 static const char tstr[] = "[|ip]"; 41 42 static const struct tok ip_option_values[] = { 43 { IPOPT_EOL, "EOL" }, 44 { IPOPT_NOP, "NOP" }, 45 { IPOPT_TS, "timestamp" }, 46 { IPOPT_SECURITY, "security" }, 47 { IPOPT_RR, "RR" }, 48 { IPOPT_SSRR, "SSRR" }, 49 { IPOPT_LSRR, "LSRR" }, 50 { IPOPT_RA, "RA" }, 51 { IPOPT_RFC1393, "traceroute" }, 52 { 0, NULL } 53 }; 54 55 /* 56 * print the recorded route in an IP RR, LSRR or SSRR option. 57 */ 58 static void 59 ip_printroute(netdissect_options *ndo, 60 register const u_char *cp, u_int length) 61 { 62 register u_int ptr; 63 register u_int len; 64 65 if (length < 3) { 66 ND_PRINT((ndo, " [bad length %u]", length)); 67 return; 68 } 69 if ((length + 1) & 3) 70 ND_PRINT((ndo, " [bad length %u]", length)); 71 ptr = cp[2] - 1; 72 if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1) 73 ND_PRINT((ndo, " [bad ptr %u]", cp[2])); 74 75 for (len = 3; len < length; len += 4) { 76 ND_PRINT((ndo, " %s", ipaddr_string(ndo, &cp[len]))); 77 if (ptr > len) 78 ND_PRINT((ndo, ",")); 79 } 80 } 81 82 /* 83 * If source-routing is present and valid, return the final destination. 84 * Otherwise, return IP destination. 85 * 86 * This is used for UDP and TCP pseudo-header in the checksum 87 * calculation. 88 */ 89 static uint32_t 90 ip_finddst(netdissect_options *ndo, 91 const struct ip *ip) 92 { 93 int length; 94 int len; 95 const u_char *cp; 96 uint32_t retval; 97 98 cp = (const u_char *)(ip + 1); 99 length = (IP_HL(ip) << 2) - sizeof(struct ip); 100 101 for (; length > 0; cp += len, length -= len) { 102 int tt; 103 104 ND_TCHECK(*cp); 105 tt = *cp; 106 if (tt == IPOPT_EOL) 107 break; 108 else if (tt == IPOPT_NOP) 109 len = 1; 110 else { 111 ND_TCHECK(cp[1]); 112 len = cp[1]; 113 if (len < 2) 114 break; 115 } 116 ND_TCHECK2(*cp, len); 117 switch (tt) { 118 119 case IPOPT_SSRR: 120 case IPOPT_LSRR: 121 if (len < 7) 122 break; 123 UNALIGNED_MEMCPY(&retval, cp + len - 4, 4); 124 return retval; 125 } 126 } 127 trunc: 128 UNALIGNED_MEMCPY(&retval, &ip->ip_dst.s_addr, sizeof(uint32_t)); 129 return retval; 130 } 131 132 /* 133 * Compute a V4-style checksum by building a pseudoheader. 134 */ 135 int 136 nextproto4_cksum(netdissect_options *ndo, 137 const struct ip *ip, const uint8_t *data, 138 u_int len, u_int covlen, u_int next_proto) 139 { 140 struct phdr { 141 uint32_t src; 142 uint32_t dst; 143 u_char mbz; 144 u_char proto; 145 uint16_t len; 146 } ph; 147 struct cksum_vec vec[2]; 148 149 /* pseudo-header.. */ 150 ph.len = htons((uint16_t)len); 151 ph.mbz = 0; 152 ph.proto = next_proto; 153 UNALIGNED_MEMCPY(&ph.src, &ip->ip_src.s_addr, sizeof(uint32_t)); 154 if (IP_HL(ip) == 5) 155 UNALIGNED_MEMCPY(&ph.dst, &ip->ip_dst.s_addr, sizeof(uint32_t)); 156 else 157 ph.dst = ip_finddst(ndo, ip); 158 159 vec[0].ptr = (const uint8_t *)(void *)&ph; 160 vec[0].len = sizeof(ph); 161 vec[1].ptr = data; 162 vec[1].len = covlen; 163 return (in_cksum(vec, 2)); 164 } 165 166 static void 167 ip_printts(netdissect_options *ndo, 168 register const u_char *cp, u_int length) 169 { 170 register u_int ptr; 171 register u_int len; 172 int hoplen; 173 const char *type; 174 175 if (length < 4) { 176 ND_PRINT((ndo, "[bad length %u]", length)); 177 return; 178 } 179 ND_PRINT((ndo, " TS{")); 180 hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4; 181 if ((length - 4) & (hoplen-1)) 182 ND_PRINT((ndo, "[bad length %u]", length)); 183 ptr = cp[2] - 1; 184 len = 0; 185 if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1) 186 ND_PRINT((ndo, "[bad ptr %u]", cp[2])); 187 switch (cp[3]&0xF) { 188 case IPOPT_TS_TSONLY: 189 ND_PRINT((ndo, "TSONLY")); 190 break; 191 case IPOPT_TS_TSANDADDR: 192 ND_PRINT((ndo, "TS+ADDR")); 193 break; 194 /* 195 * prespecified should really be 3, but some ones might send 2 196 * instead, and the IPOPT_TS_PRESPEC constant can apparently 197 * have both values, so we have to hard-code it here. 198 */ 199 200 case 2: 201 ND_PRINT((ndo, "PRESPEC2.0")); 202 break; 203 case 3: /* IPOPT_TS_PRESPEC */ 204 ND_PRINT((ndo, "PRESPEC")); 205 break; 206 default: 207 ND_PRINT((ndo, "[bad ts type %d]", cp[3]&0xF)); 208 goto done; 209 } 210 211 type = " "; 212 for (len = 4; len < length; len += hoplen) { 213 if (ptr == len) 214 type = " ^ "; 215 ND_PRINT((ndo, "%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]), 216 hoplen!=8 ? "" : ipaddr_string(ndo, &cp[len]))); 217 type = " "; 218 } 219 220 done: 221 ND_PRINT((ndo, "%s", ptr == len ? " ^ " : "")); 222 223 if (cp[3]>>4) 224 ND_PRINT((ndo, " [%d hops not recorded]} ", cp[3]>>4)); 225 else 226 ND_PRINT((ndo, "}")); 227 } 228 229 /* 230 * print IP options. 231 */ 232 static void 233 ip_optprint(netdissect_options *ndo, 234 register const u_char *cp, u_int length) 235 { 236 register u_int option_len; 237 const char *sep = ""; 238 239 for (; length > 0; cp += option_len, length -= option_len) { 240 u_int option_code; 241 242 ND_PRINT((ndo, "%s", sep)); 243 sep = ","; 244 245 ND_TCHECK(*cp); 246 option_code = *cp; 247 248 ND_PRINT((ndo, "%s", 249 tok2str(ip_option_values,"unknown %u",option_code))); 250 251 if (option_code == IPOPT_NOP || 252 option_code == IPOPT_EOL) 253 option_len = 1; 254 255 else { 256 ND_TCHECK(cp[1]); 257 option_len = cp[1]; 258 if (option_len < 2) { 259 ND_PRINT((ndo, " [bad length %u]", option_len)); 260 return; 261 } 262 } 263 264 if (option_len > length) { 265 ND_PRINT((ndo, " [bad length %u]", option_len)); 266 return; 267 } 268 269 ND_TCHECK2(*cp, option_len); 270 271 switch (option_code) { 272 case IPOPT_EOL: 273 return; 274 275 case IPOPT_TS: 276 ip_printts(ndo, cp, option_len); 277 break; 278 279 case IPOPT_RR: /* fall through */ 280 case IPOPT_SSRR: 281 case IPOPT_LSRR: 282 ip_printroute(ndo, cp, option_len); 283 break; 284 285 case IPOPT_RA: 286 if (option_len < 4) { 287 ND_PRINT((ndo, " [bad length %u]", option_len)); 288 break; 289 } 290 ND_TCHECK(cp[3]); 291 if (EXTRACT_16BITS(&cp[2]) != 0) 292 ND_PRINT((ndo, " value %u", EXTRACT_16BITS(&cp[2]))); 293 break; 294 295 case IPOPT_NOP: /* nothing to print - fall through */ 296 case IPOPT_SECURITY: 297 default: 298 break; 299 } 300 } 301 return; 302 303 trunc: 304 ND_PRINT((ndo, "%s", tstr)); 305 } 306 307 #define IP_RES 0x8000 308 309 static const struct tok ip_frag_values[] = { 310 { IP_MF, "+" }, 311 { IP_DF, "DF" }, 312 { IP_RES, "rsvd" }, /* The RFC3514 evil ;-) bit */ 313 { 0, NULL } 314 }; 315 316 struct ip_print_demux_state { 317 const struct ip *ip; 318 const u_char *cp; 319 u_int len, off; 320 u_char nh; 321 int advance; 322 }; 323 324 static void 325 ip_print_demux(netdissect_options *ndo, 326 struct ip_print_demux_state *ipds) 327 { 328 struct protoent *proto; 329 struct cksum_vec vec[1]; 330 331 again: 332 switch (ipds->nh) { 333 334 case IPPROTO_AH: 335 ipds->nh = *ipds->cp; 336 ipds->advance = ah_print(ndo, ipds->cp); 337 if (ipds->advance <= 0) 338 break; 339 ipds->cp += ipds->advance; 340 ipds->len -= ipds->advance; 341 goto again; 342 343 case IPPROTO_ESP: 344 { 345 int enh, padlen; 346 ipds->advance = esp_print(ndo, ipds->cp, ipds->len, 347 (const u_char *)ipds->ip, 348 &enh, &padlen); 349 if (ipds->advance <= 0) 350 break; 351 ipds->cp += ipds->advance; 352 ipds->len -= ipds->advance + padlen; 353 ipds->nh = enh & 0xff; 354 goto again; 355 } 356 357 case IPPROTO_IPCOMP: 358 { 359 int enh; 360 ipds->advance = ipcomp_print(ndo, ipds->cp, &enh); 361 if (ipds->advance <= 0) 362 break; 363 ipds->cp += ipds->advance; 364 ipds->len -= ipds->advance; 365 ipds->nh = enh & 0xff; 366 goto again; 367 } 368 369 case IPPROTO_SCTP: 370 sctp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len); 371 break; 372 373 case IPPROTO_DCCP: 374 dccp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len); 375 break; 376 377 case IPPROTO_TCP: 378 /* pass on the MF bit plus the offset to detect fragments */ 379 tcp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 380 ipds->off & (IP_MF|IP_OFFMASK)); 381 break; 382 383 case IPPROTO_UDP: 384 /* pass on the MF bit plus the offset to detect fragments */ 385 udp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 386 ipds->off & (IP_MF|IP_OFFMASK)); 387 break; 388 389 case IPPROTO_ICMP: 390 /* pass on the MF bit plus the offset to detect fragments */ 391 icmp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 392 ipds->off & (IP_MF|IP_OFFMASK)); 393 break; 394 395 case IPPROTO_PIGP: 396 /* 397 * XXX - the current IANA protocol number assignments 398 * page lists 9 as "any private interior gateway 399 * (used by Cisco for their IGRP)" and 88 as 400 * "EIGRP" from Cisco. 401 * 402 * Recent BSD <netinet/in.h> headers define 403 * IP_PROTO_PIGP as 9 and IP_PROTO_IGRP as 88. 404 * We define IP_PROTO_PIGP as 9 and 405 * IP_PROTO_EIGRP as 88; those names better 406 * match was the current protocol number 407 * assignments say. 408 */ 409 igrp_print(ndo, ipds->cp, ipds->len); 410 break; 411 412 case IPPROTO_EIGRP: 413 eigrp_print(ndo, ipds->cp, ipds->len); 414 break; 415 416 case IPPROTO_ND: 417 ND_PRINT((ndo, " nd %d", ipds->len)); 418 break; 419 420 case IPPROTO_EGP: 421 egp_print(ndo, ipds->cp, ipds->len); 422 break; 423 424 case IPPROTO_OSPF: 425 ospf_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip); 426 break; 427 428 case IPPROTO_IGMP: 429 igmp_print(ndo, ipds->cp, ipds->len); 430 break; 431 432 case IPPROTO_IPV4: 433 /* DVMRP multicast tunnel (ip-in-ip encapsulation) */ 434 ip_print(ndo, ipds->cp, ipds->len); 435 if (! ndo->ndo_vflag) { 436 ND_PRINT((ndo, " (ipip-proto-4)")); 437 return; 438 } 439 break; 440 441 #ifdef INET6 442 case IPPROTO_IPV6: 443 /* ip6-in-ip encapsulation */ 444 ip6_print(ndo, ipds->cp, ipds->len); 445 break; 446 #endif /*INET6*/ 447 448 case IPPROTO_RSVP: 449 rsvp_print(ndo, ipds->cp, ipds->len); 450 break; 451 452 case IPPROTO_GRE: 453 /* do it */ 454 gre_print(ndo, ipds->cp, ipds->len); 455 break; 456 457 case IPPROTO_MOBILE: 458 mobile_print(ndo, ipds->cp, ipds->len); 459 break; 460 461 case IPPROTO_PIM: 462 vec[0].ptr = ipds->cp; 463 vec[0].len = ipds->len; 464 pim_print(ndo, ipds->cp, ipds->len, in_cksum(vec, 1)); 465 break; 466 467 case IPPROTO_VRRP: 468 if (ndo->ndo_packettype == PT_CARP) { 469 if (ndo->ndo_vflag) 470 ND_PRINT((ndo, "carp %s > %s: ", 471 ipaddr_string(ndo, &ipds->ip->ip_src), 472 ipaddr_string(ndo, &ipds->ip->ip_dst))); 473 carp_print(ndo, ipds->cp, ipds->len, ipds->ip->ip_ttl); 474 } else { 475 if (ndo->ndo_vflag) 476 ND_PRINT((ndo, "vrrp %s > %s: ", 477 ipaddr_string(ndo, &ipds->ip->ip_src), 478 ipaddr_string(ndo, &ipds->ip->ip_dst))); 479 vrrp_print(ndo, ipds->cp, ipds->len, 480 (const u_char *)ipds->ip, ipds->ip->ip_ttl); 481 } 482 break; 483 484 case IPPROTO_PGM: 485 pgm_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip); 486 break; 487 488 #if defined(HAVE_NET_PFVAR_H) 489 case IPPROTO_PFSYNC: 490 pfsync_ip_print(ipds->cp, ipds->len); 491 break; 492 #endif 493 494 default: 495 if (ndo->ndo_nflag==0 && (proto = getprotobynumber(ipds->nh)) != NULL) 496 ND_PRINT((ndo, " %s", proto->p_name)); 497 else 498 ND_PRINT((ndo, " ip-proto-%d", ipds->nh)); 499 ND_PRINT((ndo, " %d", ipds->len)); 500 break; 501 } 502 } 503 504 void 505 ip_print_inner(netdissect_options *ndo, 506 const u_char *bp, 507 u_int length, u_int nh, 508 const u_char *bp2) 509 { 510 struct ip_print_demux_state ipd; 511 512 ipd.ip = (const struct ip *)bp2; 513 ipd.cp = bp; 514 ipd.len = length; 515 ipd.off = 0; 516 ipd.nh = nh; 517 ipd.advance = 0; 518 519 ip_print_demux(ndo, &ipd); 520 } 521 522 523 /* 524 * print an IP datagram. 525 */ 526 void 527 ip_print(netdissect_options *ndo, 528 const u_char *bp, 529 u_int length) 530 { 531 struct ip_print_demux_state ipd; 532 struct ip_print_demux_state *ipds=&ipd; 533 const u_char *ipend; 534 u_int hlen; 535 struct cksum_vec vec[1]; 536 uint16_t sum, ip_sum; 537 struct protoent *proto; 538 539 ipds->ip = (const struct ip *)bp; 540 ND_TCHECK(ipds->ip->ip_vhl); 541 if (IP_V(ipds->ip) != 4) { /* print version if != 4 */ 542 ND_PRINT((ndo, "IP%u ", IP_V(ipds->ip))); 543 if (IP_V(ipds->ip) == 6) 544 ND_PRINT((ndo, ", wrong link-layer encapsulation")); 545 } 546 else if (!ndo->ndo_eflag) 547 ND_PRINT((ndo, "IP ")); 548 549 ND_TCHECK(*ipds->ip); 550 if (length < sizeof (struct ip)) { 551 ND_PRINT((ndo, "truncated-ip %u", length)); 552 return; 553 } 554 hlen = IP_HL(ipds->ip) * 4; 555 if (hlen < sizeof (struct ip)) { 556 ND_PRINT((ndo, "bad-hlen %u", hlen)); 557 return; 558 } 559 560 ipds->len = EXTRACT_16BITS(&ipds->ip->ip_len); 561 if (length < ipds->len) 562 ND_PRINT((ndo, "truncated-ip - %u bytes missing! ", 563 ipds->len - length)); 564 if (ipds->len < hlen) { 565 #ifdef GUESS_TSO 566 if (ipds->len) { 567 ND_PRINT((ndo, "bad-len %u", ipds->len)); 568 return; 569 } 570 else { 571 /* we guess that it is a TSO send */ 572 ipds->len = length; 573 } 574 #else 575 ND_PRINT((ndo, "bad-len %u", ipds->len)); 576 return; 577 #endif /* GUESS_TSO */ 578 } 579 580 /* 581 * Cut off the snapshot length to the end of the IP payload. 582 */ 583 ipend = bp + ipds->len; 584 if (ipend < ndo->ndo_snapend) 585 ndo->ndo_snapend = ipend; 586 587 ipds->len -= hlen; 588 589 ipds->off = EXTRACT_16BITS(&ipds->ip->ip_off); 590 591 if (ndo->ndo_vflag) { 592 ND_PRINT((ndo, "(tos 0x%x", (int)ipds->ip->ip_tos)); 593 /* ECN bits */ 594 if (ipds->ip->ip_tos & 0x03) { 595 switch (ipds->ip->ip_tos & 0x03) { 596 case 1: 597 ND_PRINT((ndo, ",ECT(1)")); 598 break; 599 case 2: 600 ND_PRINT((ndo, ",ECT(0)")); 601 break; 602 case 3: 603 ND_PRINT((ndo, ",CE")); 604 } 605 } 606 607 if (ipds->ip->ip_ttl >= 1) 608 ND_PRINT((ndo, ", ttl %u", ipds->ip->ip_ttl)); 609 610 /* 611 * for the firewall guys, print id, offset. 612 * On all but the last stick a "+" in the flags portion. 613 * For unfragmented datagrams, note the don't fragment flag. 614 */ 615 616 ND_PRINT((ndo, ", id %u, offset %u, flags [%s], proto %s (%u)", 617 EXTRACT_16BITS(&ipds->ip->ip_id), 618 (ipds->off & 0x1fff) * 8, 619 bittok2str(ip_frag_values, "none", ipds->off&0xe000), 620 tok2str(ipproto_values,"unknown",ipds->ip->ip_p), 621 ipds->ip->ip_p)); 622 623 ND_PRINT((ndo, ", length %u", EXTRACT_16BITS(&ipds->ip->ip_len))); 624 625 if ((hlen - sizeof(struct ip)) > 0) { 626 ND_PRINT((ndo, ", options (")); 627 ip_optprint(ndo, (u_char *)(ipds->ip + 1), hlen - sizeof(struct ip)); 628 ND_PRINT((ndo, ")")); 629 } 630 631 if (!ndo->ndo_Kflag && (u_char *)ipds->ip + hlen <= ndo->ndo_snapend) { 632 vec[0].ptr = (const uint8_t *)(void *)ipds->ip; 633 vec[0].len = hlen; 634 sum = in_cksum(vec, 1); 635 if (sum != 0) { 636 ip_sum = EXTRACT_16BITS(&ipds->ip->ip_sum); 637 ND_PRINT((ndo, ", bad cksum %x (->%x)!", ip_sum, 638 in_cksum_shouldbe(ip_sum, sum))); 639 } 640 } 641 642 ND_PRINT((ndo, ")\n ")); 643 } 644 645 /* 646 * If this is fragment zero, hand it to the next higher 647 * level protocol. 648 */ 649 if ((ipds->off & 0x1fff) == 0) { 650 ipds->cp = (const u_char *)ipds->ip + hlen; 651 ipds->nh = ipds->ip->ip_p; 652 653 if (ipds->nh != IPPROTO_TCP && ipds->nh != IPPROTO_UDP && 654 ipds->nh != IPPROTO_SCTP && ipds->nh != IPPROTO_DCCP) { 655 ND_PRINT((ndo, "%s > %s: ", 656 ipaddr_string(ndo, &ipds->ip->ip_src), 657 ipaddr_string(ndo, &ipds->ip->ip_dst))); 658 } 659 ip_print_demux(ndo, ipds); 660 } else { 661 /* Ultra quiet now means that all this stuff should be suppressed */ 662 if (ndo->ndo_qflag > 1) return; 663 664 /* 665 * if this isn't the first frag, we're missing the 666 * next level protocol header. print the ip addr 667 * and the protocol. 668 */ 669 if (ipds->off & 0x1fff) { 670 ND_PRINT((ndo, "%s > %s:", ipaddr_string(ndo, &ipds->ip->ip_src), 671 ipaddr_string(ndo, &ipds->ip->ip_dst))); 672 if (!ndo->ndo_nflag && (proto = getprotobynumber(ipds->ip->ip_p)) != NULL) 673 ND_PRINT((ndo, " %s", proto->p_name)); 674 else 675 ND_PRINT((ndo, " ip-proto-%d", ipds->ip->ip_p)); 676 } 677 } 678 return; 679 680 trunc: 681 ND_PRINT((ndo, "%s", tstr)); 682 return; 683 } 684 685 void 686 ipN_print(netdissect_options *ndo, register const u_char *bp, register u_int length) 687 { 688 struct ip hdr; 689 690 if (length < 4) { 691 ND_PRINT((ndo, "truncated-ip %d", length)); 692 return; 693 } 694 memcpy (&hdr, bp, 4); 695 switch (IP_V(&hdr)) { 696 case 4: 697 ip_print (ndo, bp, length); 698 return; 699 #ifdef INET6 700 case 6: 701 ip6_print (ndo, bp, length); 702 return; 703 #endif 704 default: 705 ND_PRINT((ndo, "unknown ip %d", IP_V(&hdr))); 706 return; 707 } 708 } 709 710 /* 711 * Local Variables: 712 * c-style: whitesmith 713 * c-basic-offset: 8 714 * End: 715 */ 716 717 718