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