NETWORK ATTACKS FRAMEWORK
1.0.0
A NETwork Attacks framework. Making network attacks impact evaluation easier!
|
00001 /***************************************************************************** 00002 * 00003 * Copyright (C) 2001 Uppsala University & Ericsson AB. 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program; if not, write to the Free Software 00017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00018 * 00019 * Authors: Erik Nordstr�m, <erik.nordstrom@it.uu.se> 00020 * 00021 * 00022 *****************************************************************************/ 00023 #define NS_PORT 00024 #define OMNETPP 00025 00026 #include <stdlib.h> 00027 #include <stdarg.h> 00028 #include <sys/types.h> 00029 #include <sys/stat.h> 00030 #include <fcntl.h> 00031 #include <string.h> 00032 #include <time.h> 00033 00034 #ifdef NS_PORT 00035 00036 #ifndef OMNETPP 00037 #include "ns/aodv-uu.h" 00038 #else 00039 #include "../NA_aodv_uu_omnet.h" 00040 #endif 00041 00042 #else 00043 #include <net/if.h> 00044 #include "NA_aodv_rreq.h" 00045 #include "NA_aodv_rrep.h" 00046 #include "NA_aodv_rerr.h" 00047 #include "NA_defs_aodv.h" 00048 #include "NA_debug_aodv.h" 00049 #include "NA_params.h" 00050 #include "NA_timer_queue_aodv.h" 00051 #include "NA_routing_table.h" 00052 #endif 00053 00054 #ifndef NS_PORT 00055 extern int log_to_file, rt_log_interval; 00056 extern char *progname; 00057 int log_file_fd = -1; 00058 int log_rt_fd = -1; 00059 int log_nmsgs = 0; 00060 int debug = 0; 00061 struct timer rt_log_timer; 00062 #endif 00063 00064 void NS_CLASS log_init() 00065 { 00066 #ifndef _WIN32 00067 /* NS_PORT: Log filename is prefix + IP address + suffix */ 00068 #ifdef NS_PORT 00069 00070 char AODV_LOG_PATH[strlen(AODV_LOG_PATH_PREFIX) + 00071 strlen(AODV_LOG_PATH_SUFFIX) + 16]; 00072 char AODV_RT_LOG_PATH[strlen(AODV_LOG_PATH_PREFIX) + 00073 strlen(AODV_RT_LOG_PATH_SUFFIX) + 16]; 00074 00075 00076 sprintf(AODV_LOG_PATH, "%s%d%s", AODV_LOG_PATH_PREFIX, node_id, 00077 AODV_LOG_PATH_SUFFIX); 00078 sprintf(AODV_RT_LOG_PATH, "%s%d%s", AODV_LOG_PATH_PREFIX, node_id, 00079 AODV_RT_LOG_PATH_SUFFIX); 00080 00081 #endif /* NS_PORT */ 00082 00083 if (log_to_file) 00084 { 00085 if ((log_file_fd = 00086 open(AODV_LOG_PATH, O_RDWR | O_CREAT | O_TRUNC, 00087 S_IROTH | S_IWUSR | S_IRUSR | S_IRGRP)) < 0) 00088 { 00089 perror("open log file failed!"); 00090 exit(-1); 00091 } 00092 } 00093 if (rt_log_interval) 00094 { 00095 if ((log_rt_fd = 00096 open(AODV_RT_LOG_PATH, O_RDWR | O_CREAT | O_TRUNC, 00097 S_IROTH | S_IWUSR | S_IRUSR | S_IRGRP)) < 0) 00098 { 00099 perror("open rt log file failed!"); 00100 exit(-1); 00101 } 00102 } 00103 openlog(progname, 0, LOG_DAEMON); 00104 #endif 00105 } 00106 00107 void NS_CLASS log_rt_table_init() 00108 { 00109 timer_init(&rt_log_timer, &NS_CLASS print_rt_table, NULL); 00110 timer_set_timeout(&rt_log_timer, rt_log_interval); 00111 } 00112 00113 void NS_CLASS log_cleanup() 00114 { 00115 #ifndef _WIN32 00116 if (log_to_file && log_file_fd) 00117 { 00118 if (NS_OUTSIDE_CLASS close(log_file_fd) < 0) 00119 fprintf(stderr, "Could not close log_file_fd!\n"); 00120 } 00121 #endif 00122 } 00123 00124 void NS_CLASS write_to_log_file(char *msg, int len) 00125 { 00126 #ifndef _WIN32 00127 if (!log_file_fd) 00128 { 00129 fprintf(stderr, "Could not write to log file\n"); 00130 return; 00131 } 00132 if (len <= 0) 00133 { 00134 fprintf(stderr, "len=0\n"); 00135 return; 00136 } 00137 if (write(log_file_fd, msg, len) < 0) 00138 perror("Could not write to log file"); 00139 #endif 00140 } 00141 00142 const char *packet_type(uint32 type) 00143 { 00144 static char temp[50]; 00145 00146 switch (type) 00147 { 00148 case AODV_RREQ: 00149 return "AODV_RREQ"; 00150 case AODV_RREP: 00151 return "AODV_RREP"; 00152 case AODV_RERR: 00153 return "AODV_RERR"; 00154 default: 00155 sprintf(temp, "Unknown packet type %d", type); 00156 return temp; 00157 } 00158 } 00159 00160 void NS_CLASS alog(int type, int errnum, const char *function, const char *format, 00161 ...) 00162 { 00163 #ifndef _WIN32 00164 va_list ap; 00165 static char buffer[256] = ""; 00166 static char log_buf[1024]; 00167 char *msg; 00168 struct timeval now; 00169 struct tm *time; 00170 int len = 0; 00171 00172 /* NS_PORT: Include IP address in log */ 00173 #ifdef NS_PORT 00174 if (DEV_NR(NS_DEV_NR).enabled == 1) 00175 { 00176 len += sprintf(log_buf + len, "%s: ", 00177 ip_to_str(DEV_NR(NS_DEV_NR).ipaddr)); 00178 } 00179 #endif /* NS_PORT */ 00180 00181 va_start(ap, format); 00182 00183 if (type == LOG_WARNING) 00184 msg = &buffer[9]; 00185 else 00186 msg = buffer; 00187 00188 vsprintf(msg, format, ap); 00189 va_end(ap); 00190 00191 if (!debug && !log_to_file) 00192 goto syslog; 00193 00194 gettimeofday(&now, NULL); 00195 00196 #ifdef NS_PORT 00197 time = gmtime(&now.tv_sec); 00198 #else 00199 time = localtime(&now.tv_sec); 00200 #endif 00201 00202 /* if (type <= LOG_NOTICE) */ 00203 /* len += sprintf(log_buf + len, "%s: ", progname); */ 00204 00205 len += sprintf(log_buf + len, "%s %02d:%02d:%02d.%03ld %s: %s",nodeName, time->tm_hour, 00206 time->tm_min, time->tm_sec, now.tv_usec / 1000, function, 00207 msg); 00208 // len += sprintf(log_buf + len, "%s time = %lf - %s %s ",nodeName, simTime(), function, 00209 // msg); 00210 00211 if (errnum == 0) 00212 len += sprintf(log_buf + len, "\n"); 00213 else 00214 len += sprintf(log_buf + len, ": %s\n", strerror(errnum)); 00215 00216 if (len > 1024) 00217 { 00218 fprintf(stderr, "alog(): buffer to small! len = %d\n", len); 00219 goto syslog; 00220 } 00221 00222 /* OK, we are clear to write the buffer to the aodv log file... */ 00223 if (log_to_file) 00224 write_to_log_file(log_buf, len); 00225 00226 /* If we have the debug option set, also write to stdout */ 00227 if (debug) 00228 fputs(log_buf, stdout); 00229 00230 /* Syslog all messages that are of severity LOG_NOTICE or worse */ 00231 syslog: 00232 if (type <= LOG_NOTICE) 00233 { 00234 if (errnum != 0) 00235 { 00236 errno = errnum; 00237 syslog(type, "%s: %s: %m", function, msg); 00238 } 00239 else 00240 syslog(type, "%s: %s", function, msg); 00241 } 00242 /* Exit on error */ 00243 if (type <= LOG_ERR) 00244 exit(-1); 00245 #endif 00246 } 00247 00248 00249 char *NS_CLASS rreq_flags_to_str(RREQ * rreq) 00250 { 00251 static char buf[5]; 00252 int len = 0; 00253 char *str; 00254 00255 if (rreq->j) 00256 buf[len++] = 'J'; 00257 if (rreq->r) 00258 buf[len++] = 'R'; 00259 if (rreq->g) 00260 buf[len++] = 'G'; 00261 if (rreq->d) 00262 buf[len++] = 'D'; 00263 00264 buf[len] = '\0'; 00265 00266 str = buf; 00267 return str; 00268 } 00269 00270 char *NS_CLASS rrep_flags_to_str(RREP * rrep) 00271 { 00272 static char buf[3]; 00273 int len = 0; 00274 char *str; 00275 00276 if (rrep->r) 00277 buf[len++] = 'R'; 00278 if (rrep->a) 00279 buf[len++] = 'A'; 00280 00281 buf[len] = '\0'; 00282 00283 str = buf; 00284 return str; 00285 } 00286 00287 void NS_CLASS log_pkt_fields(AODV_msg * msg) 00288 { 00289 00290 RREQ *rreq; 00291 RREP *rrep; 00292 RERR *rerr; 00293 struct in_addr dest, orig; 00294 00295 switch (msg->type) 00296 { 00297 case AODV_RREQ: 00298 rreq = (RREQ *) msg; 00299 dest.s_addr = rreq->dest_addr; 00300 orig.s_addr = rreq->orig_addr; 00301 DEBUG(LOG_DEBUG, 0, 00302 "rreq->flags:%s rreq->hopcount=%d rreq->rreq_id=%ld", 00303 rreq_flags_to_str(rreq), rreq->hcnt, ntohl(rreq->rreq_id)); 00304 DEBUG(LOG_DEBUG, 0, "rreq->dest_addr:%s rreq->dest_seqno=%lu", 00305 ip_to_str(dest), ntohl(rreq->dest_seqno)); 00306 DEBUG(LOG_DEBUG, 0, "rreq->orig_addr:%s rreq->orig_seqno=%ld", 00307 ip_to_str(orig), ntohl(rreq->orig_seqno)); 00308 break; 00309 case AODV_RREP: 00310 rrep = (RREP *) msg; 00311 dest.s_addr = rrep->dest_addr; 00312 orig.s_addr = rrep->orig_addr; 00313 DEBUG(LOG_DEBUG, 0, "rrep->flags:%s rrep->hcnt=%d", 00314 rrep_flags_to_str(rrep), rrep->hcnt); 00315 DEBUG(LOG_DEBUG, 0, "rrep->dest_addr:%s rrep->dest_seqno=%d", 00316 ip_to_str(dest), ntohl(rrep->dest_seqno)); 00317 DEBUG(LOG_DEBUG, 0, "rrep->orig_addr:%s rrep->lifetime=%d", 00318 ip_to_str(orig), ntohl(rrep->lifetime)); 00319 break; 00320 case AODV_RERR: 00321 rerr = (RERR *) msg; 00322 DEBUG(LOG_DEBUG, 0, "rerr->dest_count:%d rerr->flags=%s", 00323 rerr->dest_count, rerr->n ? "N" : "-"); 00324 break; 00325 } 00326 } 00327 00328 char *NS_CLASS rt_flags_to_str(u_int16_t flags) 00329 { 00330 static char buf[5]; 00331 int len = 0; 00332 char *str; 00333 00334 if (flags & RT_UNIDIR) 00335 buf[len++] = 'U'; 00336 if (flags & RT_REPAIR) 00337 buf[len++] = 'R'; 00338 if (flags & RT_INET_DEST) 00339 buf[len++] = 'I'; 00340 if (flags & RT_GATEWAY) 00341 buf[len++] = 'G'; 00342 buf[len] = '\0'; 00343 00344 str = buf; 00345 return str; 00346 } 00347 00348 const char *NS_CLASS state_to_str(u_int8_t state) 00349 { 00350 if (state == VALID) 00351 return "VAL"; 00352 else if (state == INVALID) 00353 return "INV"; 00354 else 00355 return "?"; 00356 } 00357 00358 char *NS_CLASS devs_ip_to_str() 00359 { 00360 static char buf[MAX_NR_INTERFACES * IFNAMSIZ]; 00361 char *str; 00362 int i, index = 0; 00363 00364 for (i = 0; i < MAX_NR_INTERFACES; i++) 00365 { 00366 if (!DEV_NR(i).enabled) 00367 continue; 00368 index += sprintf(buf + index, "%s,", ip_to_str(DEV_NR(i).ipaddr)); 00369 } 00370 str = buf; 00371 return str; 00372 } 00373 00374 #ifndef AODV_USE_STL_RT 00375 void NS_CLASS print_rt_table(void *arg) 00376 { 00377 #ifndef _WIN32 00378 char rt_buf[2048], ifname[64], seqno_str[11]; 00379 int len = 0; 00380 int i = 0; 00381 struct timeval now; 00382 struct tm *time; 00383 00384 if (rt_tbl.num_entries == 0) 00385 goto schedule; 00386 00387 gettimeofday(&now, NULL); 00388 00389 #ifdef NS_PORT 00390 time = gmtime(&now.tv_sec); 00391 #else 00392 time = localtime(&now.tv_sec); 00393 #endif 00394 00395 len += 00396 sprintf(rt_buf, 00397 "# Time: %02d:%02d:%02d.%03ld IP: %s seqno: %u entries/active: %u/%u\n", 00398 time->tm_hour, time->tm_min, time->tm_sec, now.tv_usec / 1000, 00399 devs_ip_to_str(), this_host.seqno, rt_tbl.num_entries, 00400 rt_tbl.num_active); 00401 len += 00402 sprintf(rt_buf + len, 00403 "%-15s %-15s %-3s %-3s %-5s %-6s %-5s %-5s %-15s\n", 00404 "Destination", "Next hop", "HC", "St.", "Seqno", "Expire", 00405 "Flags", "Iface", "Precursors"); 00406 00407 write(log_rt_fd, rt_buf, len); 00408 len = 0; 00409 00410 for (i = 0; i < RT_TABLESIZE; i++) 00411 { 00412 list_t *pos; 00413 list_foreach(pos, &rt_tbl.tbl[i]) 00414 { 00415 rt_table_t *rt = (rt_table_t *) pos; 00416 00417 if (rt->dest_seqno == 0) 00418 sprintf(seqno_str, "-"); 00419 else 00420 sprintf(seqno_str, "%u", rt->dest_seqno); 00421 00422 /* Print routing table entries one by one... */ 00423 #ifdef AODV_USE_STL 00424 long dif = (1000.0*(SIMTIME_DBL(rt->rt_timer.timeout) - SIMTIME_DBL(simTime()))); 00425 00426 if (list_empty(&rt->precursors)) 00427 len += sprintf(rt_buf + len, 00428 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s\n", 00429 ip_to_str(rt->dest_addr), 00430 ip_to_str(rt->next_hop), rt->hcnt, 00431 state_to_str(rt->state), seqno_str, 00432 (rt->hcnt == 255) ? 0 : 00433 dif, 00434 rt_flags_to_str(rt->flags), 00435 if_indextoname(rt->ifindex, ifname)); 00436 00437 else 00438 { 00439 list_t *pos2; 00440 len += sprintf(rt_buf + len, 00441 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s %-15s\n", 00442 ip_to_str(rt->dest_addr), 00443 ip_to_str(rt->next_hop), rt->hcnt, 00444 state_to_str(rt->state), seqno_str, 00445 (rt->hcnt == 255) ? 0 : 00446 dif, 00447 rt_flags_to_str(rt->flags), 00448 if_indextoname(rt->ifindex, ifname), 00449 ip_to_str(((precursor_t *) rt->precursors.next)-> 00450 neighbor)); 00451 #else 00452 if (list_empty(&rt->precursors)) 00453 len += sprintf(rt_buf + len, 00454 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s\n", 00455 ip_to_str(rt->dest_addr), 00456 ip_to_str(rt->next_hop), rt->hcnt, 00457 state_to_str(rt->state), seqno_str, 00458 (rt->hcnt == 255) ? 0 : 00459 timeval_diff(&rt->rt_timer.timeout, &now), 00460 rt_flags_to_str(rt->flags), 00461 if_indextoname(rt->ifindex, ifname)); 00462 00463 else 00464 { 00465 list_t *pos2; 00466 len += sprintf(rt_buf + len, 00467 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s %-15s\n", 00468 ip_to_str(rt->dest_addr), 00469 ip_to_str(rt->next_hop), rt->hcnt, 00470 state_to_str(rt->state), seqno_str, 00471 (rt->hcnt == 255) ? 0 : 00472 timeval_diff(&rt->rt_timer.timeout, &now), 00473 rt_flags_to_str(rt->flags), 00474 if_indextoname(rt->ifindex, ifname), 00475 ip_to_str(((precursor_t *) rt->precursors.next)-> 00476 neighbor)); 00477 #endif 00478 /* Print all precursors for the current routing entry */ 00479 list_foreach(pos2, &rt->precursors) 00480 { 00481 precursor_t *pr = (precursor_t *) pos2; 00482 00483 /* Skip first entry since it is already printed */ 00484 if (pos2->prev == &rt->precursors) 00485 continue; 00486 00487 len += sprintf(rt_buf + len, "%64s %-15s\n", " ", 00488 ip_to_str(pr->neighbor)); 00489 00490 /* Since the precursor list is grown dynamically 00491 * the write buffer should be flushed for every 00492 * entry to avoid buffer overflows */ 00493 write(log_rt_fd, rt_buf, len); 00494 len = 0; 00495 00496 } 00497 } 00498 if (len > 0) 00499 { 00500 write(log_rt_fd, rt_buf, len); 00501 len = 0; 00502 } 00503 } 00504 } 00505 /* Schedule a new printing of routing table... */ 00506 schedule: 00507 timer_set_timeout(&rt_log_timer, rt_log_interval); 00508 #endif 00509 } 00510 00511 #else 00512 00513 void NS_CLASS print_rt_table(void *arg) 00514 { 00515 #ifndef _WIN32 00516 char rt_buf[2048], ifname[64], seqno_str[11]; 00517 int len = 0; 00518 struct timeval now; 00519 struct tm *time; 00520 if (rt_tbl.num_entries == 0) 00521 goto schedule; 00522 00523 gettimeofday(&now, NULL); 00524 00525 #ifdef NS_PORT 00526 time = gmtime(&now.tv_sec); 00527 #else 00528 time = localtime(&now.tv_sec); 00529 #endif 00530 00531 len += 00532 sprintf(rt_buf, 00533 "# Time: %02d:%02d:%02d.%03ld IP: %s seqno: %u entries/active: %u/%u\n", 00534 time->tm_hour, time->tm_min, time->tm_sec, now.tv_usec / 1000, 00535 devs_ip_to_str(), this_host.seqno, rt_tbl.num_entries, 00536 rt_tbl.num_active); 00537 len += 00538 sprintf(rt_buf + len, 00539 "%-15s %-15s %-3s %-3s %-5s %-6s %-5s %-5s %-15s\n", 00540 "Destination", "Next hop", "HC", "St.", "Seqno", "Expire", 00541 "Flags", "Iface", "Precursors"); 00542 00543 write(log_rt_fd, rt_buf, len); 00544 len = 0; 00545 for (AodvRtTableMap::iterator it = aodvRtTableMap.begin(); it != aodvRtTableMap.end(); it++) 00546 { 00547 rt_table_t *rt = it->second; 00548 00549 if (rt->dest_seqno == 0) 00550 sprintf(seqno_str, "-"); 00551 else 00552 sprintf(seqno_str, "%u", rt->dest_seqno); 00553 00554 /* Print routing table entries one by one... */ 00555 #ifdef AODV_USE_STL 00556 long dif = (1000.0*(SIMTIME_DBL(rt->rt_timer.timeout) - SIMTIME_DBL(simTime()))); 00557 00558 if (rt->precursors.empty()) 00559 len += sprintf(rt_buf + len, 00560 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s\n", 00561 ip_to_str(rt->dest_addr), 00562 ip_to_str(rt->next_hop), rt->hcnt, 00563 state_to_str(rt->state), seqno_str, 00564 (rt->hcnt == 255) ? 0 : 00565 dif, 00566 rt_flags_to_str(rt->flags), 00567 if_indextoname(rt->ifindex, ifname)); 00568 00569 else 00570 { 00571 00572 len += sprintf(rt_buf + len, 00573 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s %-15s\n", 00574 ip_to_str(rt->dest_addr), 00575 ip_to_str(rt->next_hop), rt->hcnt, 00576 state_to_str(rt->state), seqno_str, 00577 (rt->hcnt == 255) ? 0 : 00578 dif, 00579 rt_flags_to_str(rt->flags), 00580 if_indextoname(rt->ifindex, ifname), 00581 ip_to_str(rt->precursors[0].neighbor)); 00582 #else 00583 if (rt->precursors.empty()) 00584 len += sprintf(rt_buf + len, 00585 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s\n", 00586 ip_to_str(rt->dest_addr), 00587 ip_to_str(rt->next_hop), rt->hcnt, 00588 state_to_str(rt->state), seqno_str, 00589 (rt->hcnt == 255) ? 0 : 00590 timeval_diff(&rt->rt_timer.timeout, &now), 00591 rt_flags_to_str(rt->flags), 00592 if_indextoname(rt->ifindex, ifname)); 00593 else 00594 { 00595 len += sprintf(rt_buf + len, 00596 "%-15s %-15s %-3d %-3s %-5s %-6lu %-5s %-5s %-15s\n", 00597 ip_to_str(rt->dest_addr), 00598 ip_to_str(rt->next_hop), rt->hcnt, 00599 state_to_str(rt->state), seqno_str, 00600 (rt->hcnt == 255) ? 0 : 00601 timeval_diff(&rt->rt_timer.timeout, &now), 00602 rt_flags_to_str(rt->flags), 00603 if_indextoname(rt->ifindex, ifname), 00604 ip_to_str(((precursor_t *) rt->precursors[0].neighbor)); 00605 #endif 00606 /* Print all precursors for the current routing entry */ 00607 for (unsigned int i = 1; i< rt->precursors.size(); i++) 00608 { 00609 precursor_t *pr = &rt->precursors[i]; 00610 len += sprintf(rt_buf + len, "%64s %-15s\n", " ",ip_to_str(pr->neighbor)); 00611 00612 /* Since the precursor list is grown dynamically 00613 * the write buffer should be flushed for every 00614 * entry to avoid buffer overflows */ 00615 write(log_rt_fd, rt_buf, len); 00616 len = 0; 00617 00618 } 00619 } 00620 if (len > 0) 00621 { 00622 write(log_rt_fd, rt_buf, len); 00623 len = 0; 00624 } 00625 } 00626 /* Schedule a new printing of routing table... */ 00627 schedule: 00628 timer_set_timeout(&rt_log_timer, rt_log_interval); 00629 #endif 00630 } 00631 #endif 00632 00633 /* This function lets you print more than one IP address at the same time */ 00634 char *NS_CLASS ip_to_str(struct in_addr addr) 00635 { 00636 static char buf[16 * 4]; 00637 static int index = 0; 00638 char *str; 00639 #ifdef NS_PORT 00640 #ifndef OMNETPP 00641 #undef htonl 00642 #endif 00643 addr.s_addr = htonl(addr.s_addr); 00644 #endif 00645 #ifdef OMNETPP 00646 IPv4Address add_aux(addr.s_addr.getIPv4()); 00647 strcpy(&buf[index],add_aux.str().c_str()); 00648 #else 00649 strcpy(&buf[index], inet_ntoa(addr)); 00650 #endif 00651 str = &buf[index]; 00652 index += 16; 00653 index %= 64; 00654 return str; 00655 } 00656