| | 87 | /* Converts a string to uppercase */ |
| | 88 | void to_upper (char *s) |
| | 89 | { |
| | 90 | char *p, offset; |
| | 91 | offset = 'A' - 'a'; |
| | 92 | for(p=s;*p != '\0';p++) { |
| | 93 | if(islower(*p)) { |
| | 94 | *p += offset; |
| | 95 | } |
| | 96 | } |
| | 97 | } |
| | 98 | |
| | 99 | |
| | 100 | /* Determine radiotap data length (including header) and return offset for the |
| | 101 | beginning of the 802.11 header */ |
| | 102 | int radiotap_offset(pcap_t *p, struct pcap_pkthdr *h) |
| | 103 | { |
| | 104 | uint8_t dot11packetbuf[2312]; |
| | 105 | struct tx80211_radiotap_header *rtaphdr; |
| | 106 | int rtaphdrlen=0; |
| | 107 | |
| | 108 | /* Grab a packet to examine radiotap header */ |
| | 109 | if (pcap_next_ex(p, &h, (const u_char **)&dot11packetbuf) > -1) { |
| | 110 | |
| | 111 | rtaphdr = (struct tx80211_radiotap_header *)dot11packetbuf; |
| | 112 | rtaphdrlen = tx80211_le16(rtaphdr->it_len); /* rtap is LE */ |
| | 113 | |
| | 114 | /* Sanity check on header length */ |
| | 115 | if (rtaphdrlen > (h->len - 10)) { |
| | 116 | return -2; /* Bad radiotap data */ |
| | 117 | } |
| | 118 | |
| | 119 | return rtaphdrlen; |
| | 120 | } |
| | 121 | |
| | 122 | return -1; |
| | 123 | } |
| | 124 | |
| | 125 | void lamont_hdump(unsigned char *bp, unsigned int length) { |
| | 126 | |
| | 127 | /* stolen from tcpdump, then kludged extensively */ |
| | 128 | |
| | 129 | static const char asciify[] = "................................ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."; |
| | 130 | |
| | 131 | const unsigned short *sp; |
| | 132 | const unsigned char *ap; |
| | 133 | unsigned int i, j; |
| | 134 | int nshorts, nshorts2; |
| | 135 | int padding; |
| | 136 | |
| | 137 | printf("\n\t"); |
| | 138 | padding = 0; |
| | 139 | sp = (unsigned short *)bp; |
| | 140 | ap = (unsigned char *)bp; |
| | 141 | nshorts = (unsigned int) length / sizeof(unsigned short); |
| | 142 | nshorts2 = (unsigned int) length / sizeof(unsigned short); |
| | 143 | i = 0; |
| | 144 | j = 0; |
| | 145 | while(1) { |
| | 146 | while (--nshorts >= 0) { |
| | 147 | printf(" %04x", tx80211_ntoh16(*sp)); |
| | 148 | sp++; |
| | 149 | if ((++i % 8) == 0) |
| | 150 | break; |
| | 151 | } |
| | 152 | if (nshorts < 0) { |
| | 153 | if ((length & 1) && (((i-1) % 8) != 0)) { |
| | 154 | printf(" %02x ", *(unsigned char *)sp); |
| | 155 | padding++; |
| | 156 | } |
| | 157 | nshorts = (8 - (nshorts2 - nshorts)); |
| | 158 | while(--nshorts >= 0) { |
| | 159 | printf(" "); |
| | 160 | } |
| | 161 | if (!padding) printf(" "); |
| | 162 | } |
| | 163 | printf(" "); |
| | 164 | |
| | 165 | while (--nshorts2 >= 0) { |
| | 166 | printf("%c%c", asciify[*ap], asciify[*(ap+1)]); |
| | 167 | ap += 2; |
| | 168 | if ((++j % 8) == 0) { |
| | 169 | printf("\n\t"); |
| | 170 | break; |
| | 171 | } |
| | 172 | } |
| | 173 | if (nshorts2 < 0) { |
| | 174 | if ((length & 1) && (((j-1) % 8) != 0)) { |
| | 175 | printf("%c", asciify[*ap]); |
| | 176 | } |
| | 177 | break; |
| | 178 | } |
| | 179 | } |
| | 180 | if ((length & 1) && (((i-1) % 8) == 0)) { |
| | 181 | printf(" %02x", *(unsigned char *)sp); |
| | 182 | printf(" %c", asciify[*ap]); |
| | 183 | } |
| | 184 | printf("\n"); |
| | 185 | } |
| | 186 | |
| | 187 | /* Converts a MAC address string to a u8 array, returns -1 on error */ |
| | 188 | int string2mac (char *string, uint8_t *mac_buf) |
| | 189 | { |
| | 190 | char *ptr, *next; |
| | 191 | unsigned long val; |
| | 192 | int i; |
| | 193 | |
| | 194 | to_upper(string); |
| | 195 | |
| | 196 | ptr = next = string; |
| | 197 | for(i=0;i < 6;i++) { |
| | 198 | if((val = strtoul(next, &ptr, 16)) > 255) { |
| | 199 | return(-1); |
| | 200 | } |
| | 201 | mac_buf[i] = (unsigned char)val; |
| | 202 | if((next == ptr) && (i != 6 - 1)) { |
| | 203 | return(-1); |
| | 204 | } |
| | 205 | next = ptr + 1; |
| | 206 | } |
| | 207 | |
| | 208 | return(0); |
| | 209 | } |
| | 210 | |
| | 211 | char *printmac(unsigned char *mac) |
| | 212 | { |
| | 213 | static char macstring[18]; |
| | 214 | |
| | 215 | memset(&macstring, 0, sizeof(macstring)); |
| | 216 | (void)snprintf(macstring, sizeof(macstring), |
| | 217 | "%02x:%02x:%02x:%02x:%02x:%02x", |
| | 218 | mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| | 219 | return (macstring); |
| | 220 | } |
| | 221 | |
| | 222 | /* Look for frames matching the specified attributes, returning the delta |
| | 223 | * time. */ |
| | 224 | int watchfor(int type, int subtype, uint8_t *addr1, |
| | 225 | uint8_t *addr2, uint8_t *addr3, int flags, int timeout) { |
| | 226 | |
| | 227 | struct ieee80211_hdr *dot11hdr; |
| | 228 | |
| | 229 | struct timeval starttime, now; |
| | 230 | gettimeofday(&starttime, NULL); |
| | 231 | unsigned int elapsed; |
| | 232 | unsigned int finishtime = ((starttime.tv_sec * 1000000) + starttime.tv_usec + timeout); |
| | 233 | |
| | 234 | |
| | 235 | gettimeofday(&now, NULL); |
| | 236 | while ((unsigned int)((now.tv_sec * 1000000) + now.tv_usec) < finishtime) { |
| | 237 | if (pcap_next_ex(p, &h, (const u_char **)&packet) != 1) { |
| | 238 | return -1; |
| | 239 | } |
| | 240 | gettimeofday(&now, NULL); |
| | 241 | |
| | 242 | dot11hdr = (struct ieee80211_hdr *)packet; |
| | 243 | |
| | 244 | if (dot11hdr->u1.fc.type != type) continue; |
| | 245 | if (dot11hdr->u1.fc.subtype != subtype) continue; |
| | 246 | if ((dot11hdr->u1.fchdr & tx80211_hton16(0x00ff)) != flags) |
| | 247 | continue; |
| | 248 | if (addr1 != NULL && (memcmp(addr1, dot11hdr->addr1, 6) != 0)) |
| | 249 | continue; |
| | 250 | if (addr2 != NULL && (memcmp(addr2, dot11hdr->addr2, 6) != 0)) |
| | 251 | continue; |
| | 252 | if (addr3 != NULL && (memcmp(addr3, dot11hdr->addr3, 6) != 0)) |
| | 253 | continue; |
| | 254 | |
| | 255 | elapsed = ((unsigned int)((now.tv_sec * 1000000) + (now.tv_usec)) - (unsigned int)((starttime.tv_sec * 1000000) + (starttime.tv_usec))); |
| | 256 | return elapsed; |
| | 257 | } |
| | 258 | |
| | 259 | return 0; |
| | 260 | } |
| | 261 | |
| | 262 | /* Send a NULL data frame to the target with a false BSSID, watch for the |
| | 263 | * DEAUTH that follows. |
| | 264 | */ |
| | 265 | void l2ping_test_datainvalidbssiddeauth(tx80211_t *in_tx, int npacks, |
| | 266 | uint8_t *targetmac, uint32_t usectimeout) |
| | 267 | { |
| | 268 | int i, duration; |
| | 269 | struct lcpa_metapack *metapack; |
| | 270 | tx80211_packet_t txpack; |
| | 271 | uint8_t sourcemac[6]; |
| | 272 | uint8_t bssidmac[6]; |
| | 273 | |
| | 274 | metapack = lcpa_init(); |
| | 275 | tx80211_initpacket(&txpack); |
| | 276 | |
| | 277 | srand(time(NULL)); |
| | 278 | lcpf_randmac(sourcemac, 1); |
| | 279 | lcpf_randmac(bssidmac, 1); |
| | 280 | |
| | 281 | lcpf_80211headers(metapack, |
| | 282 | WLAN_FC_TYPE_DATA, |
| | 283 | WLAN_FC_SUBTYPE_DATANULL, |
| | 284 | 0x02, /* fcflags, FromDS */ |
| | 285 | 0x00, /* duration */ |
| | 286 | targetmac, |
| | 287 | bssidmac, |
| | 288 | sourcemac, |
| | 289 | NULL, /* addr4 */ |
| | 290 | 0, /* Fragment number */ |
| | 291 | 0); /* Sequence number */ |
| | 292 | |
| | 293 | lcpa_freeze(metapack, &txpack); |
| | 294 | lcpa_free(metapack); |
| | 295 | |
| | 296 | for(i=0; i < npacks; i++) { |
| | 297 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 298 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 299 | tx80211_geterrstr(in_tx)); |
| | 300 | return; |
| | 301 | } |
| | 302 | duration = watchfor(WLAN_FC_TYPE_MGMT, WLAN_FC_SUBTYPE_DEAUTH, |
| | 303 | bssidmac, targetmac, NULL, 0, usectimeout); |
| | 304 | if (duration > 0) { |
| | 305 | print_response(targetmac, i+1, h->len, duration); |
| | 306 | } else { |
| | 307 | print_noresponse(targetmac, i+1, usectimeout); |
| | 308 | } |
| | 309 | |
| | 310 | sleep(1); |
| | 311 | |
| | 312 | } |
| | 313 | |
| | 314 | return; |
| | 315 | } |
| | 316 | |
| | 317 | /* Send a NULL data frame to the target with a false BSSID, watch for the |
| | 318 | * ACK that follows. |
| | 319 | */ |
| | 320 | void l2ping_test_datainvalidbssid(tx80211_t *in_tx, int npacks, |
| | 321 | uint8_t *targetmac, uint32_t usectimeout) |
| | 322 | { |
| | 323 | int i, duration; |
| | 324 | struct lcpa_metapack *metapack; |
| | 325 | tx80211_packet_t txpack; |
| | 326 | uint8_t sourcemac[6]; |
| | 327 | uint8_t bssidmac[6]; |
| | 328 | |
| | 329 | metapack = lcpa_init(); |
| | 330 | tx80211_initpacket(&txpack); |
| | 331 | |
| | 332 | srand(time(NULL)); |
| | 333 | lcpf_randmac(sourcemac, 1); |
| | 334 | lcpf_randmac(bssidmac, 1); |
| | 335 | |
| | 336 | lcpf_80211headers(metapack, |
| | 337 | WLAN_FC_TYPE_DATA, |
| | 338 | WLAN_FC_SUBTYPE_DATANULL, |
| | 339 | 0x02, /* fcflags, FromDS */ |
| | 340 | 0x00, /* duration */ |
| | 341 | targetmac, |
| | 342 | bssidmac, |
| | 343 | sourcemac, |
| | 344 | NULL, /* addr4 */ |
| | 345 | 0, /* Fragment number */ |
| | 346 | 0); /* Sequence number */ |
| | 347 | |
| | 348 | lcpa_freeze(metapack, &txpack); |
| | 349 | lcpa_free(metapack); |
| | 350 | |
| | 351 | for(i=0; i < npacks; i++) { |
| | 352 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 353 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 354 | tx80211_geterrstr(in_tx)); |
| | 355 | return; |
| | 356 | } |
| | 357 | duration = watchfor(WLAN_FC_TYPE_CTRL, WLAN_FC_SUBTYPE_ACK, |
| | 358 | bssidmac, NULL, NULL, 0, usectimeout); |
| | 359 | if (duration > 0) { |
| | 360 | print_response(targetmac, i+1, h->len, duration); |
| | 361 | } else { |
| | 362 | print_noresponse(targetmac, i+1, usectimeout); |
| | 363 | } |
| | 364 | |
| | 365 | sleep(1); |
| | 366 | |
| | 367 | } |
| | 368 | |
| | 369 | return; |
| | 370 | } |
| | 371 | |
| | 372 | /* |
| | 373 | * Send a NULL data frame to the AP target with a valid BSSID, valid source |
| | 374 | * and a destination multicast address. Watch for the multicast data |
| | 375 | * frame response with FromDS set that follows. |
| | 376 | */ |
| | 377 | void l2ping_test_nulldatamcast(tx80211_t *in_tx, int npacks, |
| | 378 | uint8_t *bssid, uint8_t *source, uint32_t usectimeout) |
| | 379 | { |
| | 380 | int i, duration; |
| | 381 | struct lcpa_metapack *metapack; |
| | 382 | tx80211_packet_t txpack; |
| | 383 | uint8_t mcastdest[6]; |
| | 384 | |
| | 385 | metapack = lcpa_init(); |
| | 386 | tx80211_initpacket(&txpack); |
| | 387 | |
| | 388 | srand(time(NULL)); |
| | 389 | lcpf_randmac(mcastdest, 1); |
| | 390 | mcastdest[0] = 0xff; /* multicast bit set */ |
| | 391 | |
| | 392 | lcpf_80211headers(metapack, |
| | 393 | WLAN_FC_TYPE_DATA, |
| | 394 | WLAN_FC_SUBTYPE_DATANULL, |
| | 395 | 0x01, /* fcflags, ToDS */ |
| | 396 | 0x00, /* duration */ |
| | 397 | bssid, /* target */ |
| | 398 | source, |
| | 399 | mcastdest, |
| | 400 | NULL, /* addr4 */ |
| | 401 | 0, /* Fragment number */ |
| | 402 | 0); /* Sequence number */ |
| | 403 | |
| | 404 | lcpa_freeze(metapack, &txpack); |
| | 405 | lcpa_free(metapack); |
| | 406 | |
| | 407 | for(i=0; i < npacks; i++) { |
| | 408 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 409 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 410 | tx80211_geterrstr(in_tx)); |
| | 411 | return; |
| | 412 | } |
| | 413 | duration = watchfor(WLAN_FC_TYPE_DATA, WLAN_FC_SUBTYPE_DATANULL, |
| | 414 | mcastdest, bssid, source, 0, usectimeout); |
| | 415 | if (duration > 0) { |
| | 416 | print_response(bssid, i+1, h->len, duration); |
| | 417 | } else { |
| | 418 | print_noresponse(bssid, i+1, usectimeout); |
| | 419 | } |
| | 420 | |
| | 421 | sleep(1); |
| | 422 | |
| | 423 | } |
| | 424 | |
| | 425 | return; |
| | 426 | } |
| | 427 | |
| | 428 | /* |
| | 429 | * Send a NULL data frame to the target with a valid BSSID, watch for the |
| | 430 | * ACK that follows. |
| | 431 | */ |
| | 432 | void l2ping_test_datavalidbssid(tx80211_t *in_tx, int npacks, |
| | 433 | uint8_t *targetmac, uint8_t *bssid, uint32_t usectimeout) |
| | 434 | { |
| | 435 | int i, duration; |
| | 436 | struct lcpa_metapack *metapack; |
| | 437 | tx80211_packet_t txpack; |
| | 438 | uint8_t sourcemac[6]; |
| | 439 | |
| | 440 | metapack = lcpa_init(); |
| | 441 | tx80211_initpacket(&txpack); |
| | 442 | |
| | 443 | srand(time(NULL)); |
| | 444 | lcpf_randmac(sourcemac, 1); |
| | 445 | |
| | 446 | lcpf_80211headers(metapack, |
| | 447 | WLAN_FC_TYPE_DATA, |
| | 448 | WLAN_FC_SUBTYPE_DATANULL, |
| | 449 | 0x02, /* fcflags, FromDS */ |
| | 450 | 0x00, /* duration */ |
| | 451 | targetmac, |
| | 452 | bssid, |
| | 453 | sourcemac, |
| | 454 | NULL, /* addr4 */ |
| | 455 | 0, /* Fragment number */ |
| | 456 | 0); /* Sequence number */ |
| | 457 | |
| | 458 | lcpa_freeze(metapack, &txpack); |
| | 459 | lcpa_free(metapack); |
| | 460 | |
| | 461 | for(i=0; i < npacks; i++) { |
| | 462 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 463 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 464 | tx80211_geterrstr(in_tx)); |
| | 465 | return; |
| | 466 | } |
| | 467 | duration = watchfor(WLAN_FC_TYPE_CTRL, WLAN_FC_SUBTYPE_ACK, |
| | 468 | bssid, NULL, NULL, 0, usectimeout); |
| | 469 | if (duration > 0) { |
| | 470 | print_response(targetmac, i+1, h->len, duration); |
| | 471 | } else { |
| | 472 | print_noresponse(targetmac, i+1, usectimeout); |
| | 473 | } |
| | 474 | |
| | 475 | sleep(1); |
| | 476 | |
| | 477 | } |
| | 478 | |
| | 479 | return; |
| | 480 | } |
| | 481 | |
| | 482 | /* Send an RTS frame to the target station, watch for CTS response */ |
| | 483 | void l2ping_test_rtscts(tx80211_t *in_tx, int npacks, uint8_t *targetmac, |
| | 484 | uint32_t usectimeout) |
| | 485 | { |
| | 486 | int i, duration; |
| | 487 | struct lcpa_metapack *metapack; |
| | 488 | tx80211_packet_t txpack; |
| | 489 | uint8_t transmittermac[6]; |
| | 490 | |
| | 491 | metapack = lcpa_init(); |
| | 492 | tx80211_initpacket(&txpack); |
| | 493 | |
| | 494 | srand(time(NULL)); |
| | 495 | lcpf_randmac(transmittermac, 1); |
| | 496 | |
| | 497 | lcpf_rts(metapack, |
| | 498 | targetmac, |
| | 499 | transmittermac, |
| | 500 | 0x00, /* fcflags */ |
| | 501 | 0x00); /* duration */ |
| | 502 | |
| | 503 | lcpa_freeze(metapack, &txpack); |
| | 504 | lcpa_free(metapack); |
| | 505 | |
| | 506 | //lamont_hdump(txpack.packet, txpack.plen); |
| | 507 | |
| | 508 | for(i=0; i < npacks; i++) { |
| | 509 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 510 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 511 | tx80211_geterrstr(in_tx)); |
| | 512 | return; |
| | 513 | } |
| | 514 | duration = watchfor(WLAN_FC_TYPE_CTRL, WLAN_FC_SUBTYPE_CTS, |
| | 515 | transmittermac, NULL, NULL, 0, usectimeout); |
| | 516 | if (duration > 0) { |
| | 517 | print_response(targetmac, i+1, h->len, duration); |
| | 518 | } else { |
| | 519 | print_noresponse(targetmac, i+1, usectimeout); |
| | 520 | } |
| | 521 | sleep(1); |
| | 522 | |
| | 523 | } |
| | 524 | |
| | 525 | return; |
| | 526 | } |
| | 527 | |
| | 528 | /* |
| | 529 | * Send a NULL data frame to the AP target with a valid BSSID, invalid source |
| | 530 | * and a destination broadcast address. Watch for the deauth |
| | 531 | * frame response from the AP. |
| | 532 | */ |
| | 533 | void l2ping_test_nulldatainvalidsrc(tx80211_t *in_tx, int npacks, |
| | 534 | uint8_t *targetmac, uint32_t usectimeout) |
| | 535 | { |
| | 536 | int i, duration; |
| | 537 | struct lcpa_metapack *metapack; |
| | 538 | tx80211_packet_t txpack; |
| | 539 | uint8_t invalidsource[6]; |
| | 540 | uint8_t broadcastdest[] = "\xff\xff\xff\xff\xff\xff"; |
| | 541 | |
| | 542 | metapack = lcpa_init(); |
| | 543 | tx80211_initpacket(&txpack); |
| | 544 | |
| | 545 | srand(time(NULL)); |
| | 546 | lcpf_randmac(invalidsource, 1); |
| | 547 | |
| | 548 | lcpf_80211headers(metapack, |
| | 549 | WLAN_FC_TYPE_DATA, |
| | 550 | WLAN_FC_SUBTYPE_DATANULL, |
| | 551 | 0x01, /* fcflags, ToDS */ |
| | 552 | 0x00, /* duration */ |
| | 553 | targetmac, /* target/BSSID */ |
| | 554 | invalidsource, |
| | 555 | broadcastdest, |
| | 556 | NULL, /* addr4 */ |
| | 557 | 0, /* Fragment number */ |
| | 558 | 0); /* Sequence number */ |
| | 559 | |
| | 560 | lcpa_freeze(metapack, &txpack); |
| | 561 | lcpa_free(metapack); |
| | 562 | |
| | 563 | //lamont_hdump(txpack.packet, txpack.plen); |
| | 564 | |
| | 565 | for(i=0; i < npacks; i++) { |
| | 566 | if (tx80211_txpacket(in_tx, &txpack) < 0) { |
| | 567 | fprintf(stderr, "Unable to inject packet: %s\n", |
| | 568 | tx80211_geterrstr(in_tx)); |
| | 569 | return; |
| | 570 | } |
| | 571 | duration = watchfor(WLAN_FC_TYPE_MGMT, WLAN_FC_SUBTYPE_DEAUTH, |
| | 572 | invalidsource, targetmac, targetmac, 0, |
| | 573 | usectimeout); |
| | 574 | if (duration > 0) { |
| | 575 | print_response(targetmac, i+1, h->len, duration); |
| | 576 | } else { |
| | 577 | print_noresponse(targetmac, i+1, usectimeout); |
| | 578 | } |
| | 579 | |
| | 580 | sleep(1); |
| | 581 | |
| | 582 | } |
| | 583 | |
| | 584 | return; |
| | 585 | } |
| | 586 | void print_noresponse(uint8_t *mac, int num, int timeout) |
| | 587 | { |
| | 588 | printf("No response from %s : num=%d time=%d usec\n", |
| | 589 | printmac(mac), num, timeout); |
| | 590 | } |
| | 591 | void print_response(uint8_t *mac, int num, int len, int time) |
| | 592 | { |
| | 593 | /* Set exitval to 0 since we got a response */ |
| | 594 | exitval = 0; |
| | 595 | printf("%d bytes from %s : num=%d time=%d usec\n", |
| | 596 | len, printmac(mac), num, time); |
| | 597 | } |
| | 598 | |
| | 599 | void print_test_detail() { |
| | 600 | int i=0; |
| | 601 | while(testcases[i].testdesc != NULL) { |
| | 602 | printf("Test Case:\t%d\n", testcases[i].testnum); |
| | 603 | printf("Test Name:\t%s\n", testcases[i].testname); |
| | 604 | printf("Test Desc:\t%s\n", testcases[i].testdesc); |
| | 605 | printf("Test Reqs:\t"); |
| | 606 | if (testcases[i].asmac) printf("source (-S) "); |
| | 607 | if (testcases[i].admac) printf("dest (-D) "); |
| | 608 | if (testcases[i].abmac) printf("BSSID (-B) "); |
| | 609 | if (!(testcases[i].abmac || testcases[i].admac, |
| | 610 | testcases[i].asmac)) { |
| | 611 | printf("None."); |
| | 612 | } |
| | 613 | printf("\n\n"); |
| | 614 | i++; |
| | 615 | } |
| | 616 | } |
| | 617 | |
| | 618 | |