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 22 #include <sys/cdefs.h> 23 #ifndef lint 24 __RCSID("$NetBSD: print-ip.c,v 1.8 2015/03/31 21:59:35 christos Exp $"); 25 #endif 26 27 #define NETDISSECT_REWORKED 28 #ifdef HAVE_CONFIG_H 29 #include "config.h" 30 #endif 31 32 #include <tcpdump-stdinc.h> 33 34 #include <string.h> 35 36 #include "interface.h" 37 #include "addrtoname.h" 38 #include "extract.h" /* must come after interface.h */ 39 40 #include "ip.h" 41 #include "ipproto.h" 42 43 static const char tstr[] = "[|ip]"; 44 45 static const struct tok ip_option_values[] = { 46 { IPOPT_EOL, "EOL" }, 47 { IPOPT_NOP, "NOP" }, 48 { IPOPT_TS, "timestamp" }, 49 { IPOPT_SECURITY, "security" }, 50 { IPOPT_RR, "RR" }, 51 { IPOPT_SSRR, "SSRR" }, 52 { IPOPT_LSRR, "LSRR" }, 53 { IPOPT_RA, "RA" }, 54 { IPOPT_RFC1393, "traceroute" }, 55 { 0, NULL } 56 }; 57 58 /* 59 * print the recorded route in an IP RR, LSRR or SSRR option. 60 */ 61 static void 62 ip_printroute(netdissect_options *ndo, 63 register const u_char *cp, u_int length) 64 { 65 register u_int ptr; 66 register u_int len; 67 68 if (length < 3) { 69 ND_PRINT((ndo, " [bad length %u]", length)); 70 return; 71 } 72 if ((length + 1) & 3) 73 ND_PRINT((ndo, " [bad length %u]", length)); 74 ptr = cp[2] - 1; 75 if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1) 76 ND_PRINT((ndo, " [bad ptr %u]", cp[2])); 77 78 for (len = 3; len < length; len += 4) { 79 ND_PRINT((ndo, " %s", ipaddr_string(ndo, &cp[len]))); 80 if (ptr > len) 81 ND_PRINT((ndo, ",")); 82 } 83 } 84 85 /* 86 * If source-routing is present and valid, return the final destination. 87 * Otherwise, return IP destination. 88 * 89 * This is used for UDP and TCP pseudo-header in the checksum 90 * calculation. 91 */ 92 static uint32_t 93 ip_finddst(netdissect_options *ndo, 94 const struct ip *ip) 95 { 96 int length; 97 int len; 98 const u_char *cp; 99 uint32_t retval; 100 101 cp = (const u_char *)(ip + 1); 102 length = (IP_HL(ip) << 2) - sizeof(struct ip); 103 104 for (; length > 0; cp += len, length -= len) { 105 int tt; 106 107 ND_TCHECK(*cp); 108 tt = *cp; 109 if (tt == IPOPT_EOL) 110 break; 111 else if (tt == IPOPT_NOP) 112 len = 1; 113 else { 114 ND_TCHECK(cp[1]); 115 len = cp[1]; 116 if (len < 2) 117 break; 118 } 119 ND_TCHECK2(*cp, len); 120 switch (tt) { 121 122 case IPOPT_SSRR: 123 case IPOPT_LSRR: 124 if (len < 7) 125 break; 126 UNALIGNED_MEMCPY(&retval, cp + len - 4, 4); 127 return retval; 128 } 129 } 130 trunc: 131 UNALIGNED_MEMCPY(&retval, &ip->ip_dst.s_addr, sizeof(uint32_t)); 132 return retval; 133 } 134 135 /* 136 * Compute a V4-style checksum by building a pseudoheader. 137 */ 138 int 139 nextproto4_cksum(netdissect_options *ndo, 140 const struct ip *ip, const uint8_t *data, 141 u_int len, u_int covlen, u_int next_proto) 142 { 143 struct phdr { 144 uint32_t src; 145 uint32_t dst; 146 u_char mbz; 147 u_char proto; 148 uint16_t len; 149 } ph; 150 struct cksum_vec vec[2]; 151 152 /* pseudo-header.. */ 153 ph.len = htons((uint16_t)len); 154 ph.mbz = 0; 155 ph.proto = next_proto; 156 UNALIGNED_MEMCPY(&ph.src, &ip->ip_src.s_addr, sizeof(uint32_t)); 157 if (IP_HL(ip) == 5) 158 UNALIGNED_MEMCPY(&ph.dst, &ip->ip_dst.s_addr, sizeof(uint32_t)); 159 else 160 ph.dst = ip_finddst(ndo, ip); 161 162 vec[0].ptr = (const uint8_t *)(void *)&ph; 163 vec[0].len = sizeof(ph); 164 vec[1].ptr = data; 165 vec[1].len = covlen; 166 return (in_cksum(vec, 2)); 167 } 168 169 static void 170 ip_printts(netdissect_options *ndo, 171 register const u_char *cp, u_int length) 172 { 173 register u_int ptr; 174 register u_int len; 175 int hoplen; 176 const char *type; 177 178 if (length < 4) { 179 ND_PRINT((ndo, "[bad length %u]", length)); 180 return; 181 } 182 ND_PRINT((ndo, " TS{")); 183 hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4; 184 if ((length - 4) & (hoplen-1)) 185 ND_PRINT((ndo, "[bad length %u]", length)); 186 ptr = cp[2] - 1; 187 len = 0; 188 if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1) 189 ND_PRINT((ndo, "[bad ptr %u]", cp[2])); 190 switch (cp[3]&0xF) { 191 case IPOPT_TS_TSONLY: 192 ND_PRINT((ndo, "TSONLY")); 193 break; 194 case IPOPT_TS_TSANDADDR: 195 ND_PRINT((ndo, "TS+ADDR")); 196 break; 197 /* 198 * prespecified should really be 3, but some ones might send 2 199 * instead, and the IPOPT_TS_PRESPEC constant can apparently 200 * have both values, so we have to hard-code it here. 201 */ 202 203 case 2: 204 ND_PRINT((ndo, "PRESPEC2.0")); 205 break; 206 case 3: /* IPOPT_TS_PRESPEC */ 207 ND_PRINT((ndo, "PRESPEC")); 208 break; 209 default: 210 ND_PRINT((ndo, "[bad ts type %d]", cp[3]&0xF)); 211 goto done; 212 } 213 214 type = " "; 215 for (len = 4; len < length; len += hoplen) { 216 if (ptr == len) 217 type = " ^ "; 218 ND_PRINT((ndo, "%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]), 219 hoplen!=8 ? "" : ipaddr_string(ndo, &cp[len]))); 220 type = " "; 221 } 222 223 done: 224 ND_PRINT((ndo, "%s", ptr == len ? " ^ " : "")); 225 226 if (cp[3]>>4) 227 ND_PRINT((ndo, " [%d hops not recorded]} ", cp[3]>>4)); 228 else 229 ND_PRINT((ndo, "}")); 230 } 231 232 /* 233 * print IP options. 234 */ 235 static void 236 ip_optprint(netdissect_options *ndo, 237 register const u_char *cp, u_int length) 238 { 239 register u_int option_len; 240 const char *sep = ""; 241 242 for (; length > 0; cp += option_len, length -= option_len) { 243 u_int option_code; 244 245 ND_PRINT((ndo, "%s", sep)); 246 sep = ","; 247 248 ND_TCHECK(*cp); 249 option_code = *cp; 250 251 ND_PRINT((ndo, "%s", 252 tok2str(ip_option_values,"unknown %u",option_code))); 253 254 if (option_code == IPOPT_NOP || 255 option_code == IPOPT_EOL) 256 option_len = 1; 257 258 else { 259 ND_TCHECK(cp[1]); 260 option_len = cp[1]; 261 if (option_len < 2) { 262 ND_PRINT((ndo, " [bad length %u]", option_len)); 263 return; 264 } 265 } 266 267 if (option_len > length) { 268 ND_PRINT((ndo, " [bad length %u]", option_len)); 269 return; 270 } 271 272 ND_TCHECK2(*cp, option_len); 273 274 switch (option_code) { 275 case IPOPT_EOL: 276 return; 277 278 case IPOPT_TS: 279 ip_printts(ndo, cp, option_len); 280 break; 281 282 case IPOPT_RR: /* fall through */ 283 case IPOPT_SSRR: 284 case IPOPT_LSRR: 285 ip_printroute(ndo, cp, option_len); 286 break; 287 288 case IPOPT_RA: 289 if (option_len < 4) { 290 ND_PRINT((ndo, " [bad length %u]", option_len)); 291 break; 292 } 293 ND_TCHECK(cp[3]); 294 if (EXTRACT_16BITS(&cp[2]) != 0) 295 ND_PRINT((ndo, " value %u", EXTRACT_16BITS(&cp[2]))); 296 break; 297 298 case IPOPT_NOP: /* nothing to print - fall through */ 299 case IPOPT_SECURITY: 300 default: 301 break; 302 } 303 } 304 return; 305 306 trunc: 307 ND_PRINT((ndo, "%s", tstr)); 308 } 309 310 #define IP_RES 0x8000 311 312 static const struct tok ip_frag_values[] = { 313 { IP_MF, "+" }, 314 { IP_DF, "DF" }, 315 { IP_RES, "rsvd" }, /* The RFC3514 evil ;-) bit */ 316 { 0, NULL } 317 }; 318 319 struct ip_print_demux_state { 320 const struct ip *ip; 321 const u_char *cp; 322 u_int len, off; 323 u_char nh; 324 int advance; 325 }; 326 327 static void 328 ip_print_demux(netdissect_options *ndo, 329 struct ip_print_demux_state *ipds) 330 { 331 struct protoent *proto; 332 struct cksum_vec vec[1]; 333 334 again: 335 switch (ipds->nh) { 336 337 case IPPROTO_AH: 338 ipds->nh = *ipds->cp; 339 ipds->advance = ah_print(ndo, ipds->cp); 340 if (ipds->advance <= 0) 341 break; 342 ipds->cp += ipds->advance; 343 ipds->len -= ipds->advance; 344 goto again; 345 346 case IPPROTO_ESP: 347 { 348 int enh, padlen; 349 ipds->advance = esp_print(ndo, ipds->cp, ipds->len, 350 (const u_char *)ipds->ip, 351 &enh, &padlen); 352 if (ipds->advance <= 0) 353 break; 354 ipds->cp += ipds->advance; 355 ipds->len -= ipds->advance + padlen; 356 ipds->nh = enh & 0xff; 357 goto again; 358 } 359 360 case IPPROTO_IPCOMP: 361 { 362 int enh; 363 ipds->advance = ipcomp_print(ndo, ipds->cp, &enh); 364 if (ipds->advance <= 0) 365 break; 366 ipds->cp += ipds->advance; 367 ipds->len -= ipds->advance; 368 ipds->nh = enh & 0xff; 369 goto again; 370 } 371 372 case IPPROTO_SCTP: 373 sctp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len); 374 break; 375 376 case IPPROTO_DCCP: 377 dccp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len); 378 break; 379 380 case IPPROTO_TCP: 381 /* pass on the MF bit plus the offset to detect fragments */ 382 tcp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 383 ipds->off & (IP_MF|IP_OFFMASK)); 384 break; 385 386 case IPPROTO_UDP: 387 /* pass on the MF bit plus the offset to detect fragments */ 388 udp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 389 ipds->off & (IP_MF|IP_OFFMASK)); 390 break; 391 392 case IPPROTO_ICMP: 393 /* pass on the MF bit plus the offset to detect fragments */ 394 icmp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip, 395 ipds->off & (IP_MF|IP_OFFMASK)); 396 break; 397 398 case IPPROTO_PIGP: 399 /* 400 * XXX - the current IANA protocol number assignments 401 * page lists 9 as "any private interior gateway 402 * (used by Cisco for their IGRP)" and 88 as 403 * "EIGRP" from Cisco. 404 * 405 * Recent BSD <netinet/in.h> headers define 406 * IP_PROTO_PIGP as 9 and IP_PROTO_IGRP as 88. 407 * We define IP_PROTO_PIGP as 9 and 408 * IP_PROTO_EIGRP as 88; those names better 409 * match was the current protocol number 410 * assignments say. 411 */ 412 igrp_print(ndo, ipds->cp, ipds->len); 413 break; 414 415 case IPPROTO_EIGRP: 416 eigrp_print(ndo, ipds->cp, ipds->len); 417 break; 418 419 case IPPROTO_ND: 420 ND_PRINT((ndo, " nd %d", ipds->len)); 421 break; 422 423 case IPPROTO_EGP: 424 egp_print(ndo, ipds->cp, ipds->len); 425 break; 426 427 case IPPROTO_OSPF: 428 ospf_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip); 429 break; 430 431 case IPPROTO_IGMP: 432 igmp_print(ndo, ipds->cp, ipds->len); 433 break; 434 435 case IPPROTO_IPV4: 436 /* DVMRP multicast tunnel (ip-in-ip encapsulation) */ 437 ip_print(ndo, ipds->cp, ipds->len); 438 if (! ndo->ndo_vflag) { 439 ND_PRINT((ndo, " (ipip-proto-4)")); 440 return; 441 } 442 break; 443 444 case IPPROTO_IPV6: 445 /* ip6-in-ip encapsulation */ 446 ip6_print(ndo, ipds->cp, ipds->len); 447 break; 448 449 case IPPROTO_RSVP: 450 rsvp_print(ndo, ipds->cp, ipds->len); 451 break; 452 453 case IPPROTO_GRE: 454 /* do it */ 455 gre_print(ndo, ipds->cp, ipds->len); 456 break; 457 458 case IPPROTO_MOBILE: 459 mobile_print(ndo, ipds->cp, ipds->len); 460 break; 461 462 case IPPROTO_PIM: 463 vec[0].ptr = ipds->cp; 464 vec[0].len = ipds->len; 465 pim_print(ndo, ipds->cp, ipds->len, in_cksum(vec, 1)); 466 break; 467 468 case IPPROTO_VRRP: 469 if (ndo->ndo_packettype == PT_CARP) { 470 if (ndo->ndo_vflag) 471 ND_PRINT((ndo, "carp %s > %s: ", 472 ipaddr_string(ndo, &ipds->ip->ip_src), 473 ipaddr_string(ndo, &ipds->ip->ip_dst))); 474 carp_print(ndo, ipds->cp, ipds->len, ipds->ip->ip_ttl); 475 } else { 476 if (ndo->ndo_vflag) 477 ND_PRINT((ndo, "vrrp %s > %s: ", 478 ipaddr_string(ndo, &ipds->ip->ip_src), 479 ipaddr_string(ndo, &ipds->ip->ip_dst))); 480 vrrp_print(ndo, ipds->cp, ipds->len, 481 (const u_char *)ipds->ip, ipds->ip->ip_ttl); 482 } 483 break; 484 485 case IPPROTO_PGM: 486 pgm_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip); 487 break; 488 489 case IPPROTO_PFSYNC: 490 pfsync_ip_print(ipds->cp, ipds->len, (const u_char *)ipds->ip); 491 break; 492 493 default: 494 if (ndo->ndo_nflag==0 && (proto = getprotobynumber(ipds->nh)) != NULL) 495 ND_PRINT((ndo, " %s", proto->p_name)); 496 else 497 ND_PRINT((ndo, " ip-proto-%d", ipds->nh)); 498 ND_PRINT((ndo, " %d", ipds->len)); 499 break; 500 } 501 } 502 503 void 504 ip_print_inner(netdissect_options *ndo, 505 const u_char *bp, 506 u_int length, u_int nh, 507 const u_char *bp2) 508 { 509 struct ip_print_demux_state ipd; 510 511 ipd.ip = (const struct ip *)bp2; 512 ipd.cp = bp; 513 ipd.len = length; 514 ipd.off = 0; 515 ipd.nh = nh; 516 ipd.advance = 0; 517 518 ip_print_demux(ndo, &ipd); 519 } 520 521 522 /* 523 * print an IP datagram. 524 */ 525 void 526 ip_print(netdissect_options *ndo, 527 const u_char *bp, 528 u_int length) 529 { 530 struct ip_print_demux_state ipd; 531 struct ip_print_demux_state *ipds=&ipd; 532 const u_char *ipend; 533 u_int hlen; 534 struct cksum_vec vec[1]; 535 uint16_t sum, ip_sum; 536 struct protoent *proto; 537 538 ipds->ip = (const struct ip *)bp; 539 ND_TCHECK(ipds->ip->ip_vhl); 540 if (IP_V(ipds->ip) != 4) { /* print version if != 4 */ 541 if (IP_V(ipds->ip) == 6) 542 ND_PRINT((ndo, "IP6, wrong link-layer encapsulation ")); 543 else 544 ND_PRINT((ndo, "IP%u ", IP_V(ipds->ip))); 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 case 6: 700 ip6_print (ndo, bp, length); 701 return; 702 default: 703 ND_PRINT((ndo, "unknown ip %d", IP_V(&hdr))); 704 return; 705 } 706 } 707 708 /* 709 * Local Variables: 710 * c-style: whitesmith 711 * c-basic-offset: 8 712 * End: 713 */ 714 715 716