1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <fcntl.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <exacct.h> 31 #include <libdladm.h> 32 33 #define TIMEBUFLEN 20 34 #define GBIT 1000000000 35 #define MBIT 1000000 36 #define KBIT 1000 37 38 #define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \ 39 (step) = 1; \ 40 (tbytes) = 0; \ 41 (ttime) = 0; \ 42 (tibytes) = 0; \ 43 (tobytes) = 0; \ 44 } 45 46 /* Flow/Link Descriptor */ 47 typedef struct net_desc_s { 48 char net_desc_name[LIFNAMSIZ]; 49 char net_desc_devname[LIFNAMSIZ]; 50 uchar_t net_desc_ehost[ETHERADDRL]; 51 uchar_t net_desc_edest[ETHERADDRL]; 52 ushort_t net_desc_vlan_tpid; 53 ushort_t net_desc_vlan_tci; 54 ushort_t net_desc_sap; 55 ushort_t net_desc_cpuid; 56 ushort_t net_desc_priority; 57 uint64_t net_desc_bw_limit; 58 in6_addr_t net_desc_saddr; 59 in6_addr_t net_desc_daddr; 60 boolean_t net_desc_isv4; 61 in_port_t net_desc_sport; 62 in_port_t net_desc_dport; 63 uint8_t net_desc_protocol; 64 uint8_t net_desc_dsfield; 65 boolean_t net_desc_newrec; 66 } net_desc_t; 67 68 /* Time structure: Year, Month, Day, Hour, Min, Sec */ 69 typedef struct net_time_s { 70 int net_time_yr; 71 int net_time_mon; 72 int net_time_day; 73 int net_time_hr; 74 int net_time_min; 75 int net_time_sec; 76 } net_time_t; 77 78 /* Flow/Link Stats */ 79 typedef struct net_stat_s { 80 char net_stat_name[LIFNAMSIZ]; 81 uint64_t net_stat_ibytes; 82 uint64_t net_stat_obytes; 83 uint64_t net_stat_ipackets; 84 uint64_t net_stat_opackets; 85 uint64_t net_stat_ierrors; 86 uint64_t net_stat_oerrors; 87 uint64_t net_stat_tibytes; 88 uint64_t net_stat_tobytes; 89 uint64_t net_stat_tipackets; 90 uint64_t net_stat_topackets; 91 uint64_t net_stat_tierrors; 92 uint64_t net_stat_toerrors; 93 uint64_t net_stat_ctime; 94 uint64_t net_stat_tdiff; 95 net_time_t net_stat_time; 96 struct net_stat_s *net_stat_next; 97 net_desc_t *net_stat_desc; 98 boolean_t net_stat_isref; 99 } net_stat_t; 100 101 /* Used to create the [gnu]plot file */ 102 typedef struct net_plot_entry_s { 103 char *net_pe_name; 104 uint64_t net_pe_tottime; 105 uint64_t net_pe_totbytes; 106 uint64_t net_pe_totibytes; 107 uint64_t net_pe_totobytes; 108 uint64_t net_pe_lasttime; 109 } net_plot_entry_t; 110 111 /* Stats entry */ 112 typedef struct net_entry_s { 113 net_desc_t *net_entry_desc; 114 net_stat_t *net_entry_shead; 115 net_stat_t *net_entry_stail; 116 int net_entry_scount; 117 net_stat_t *net_entry_sref; 118 net_stat_t *net_entry_tstats; 119 uint64_t net_entry_ttime; 120 struct net_entry_s *net_entry_next; 121 } net_entry_t; 122 123 /* Time sorted list */ 124 typedef struct net_time_entry_s { 125 net_stat_t *my_time_stat; 126 struct net_time_entry_s *net_time_entry_next; 127 struct net_time_entry_s *net_time_entry_prev; 128 } net_time_entry_t; 129 130 /* The parsed table */ 131 typedef struct net_table_s { 132 /* List of stats */ 133 net_entry_t *net_table_head; 134 net_entry_t *net_table_tail; 135 int net_entries; 136 137 /* 138 * Optimization I : List sorted by time, i.e: 139 * Time Resource .. 140 * ------------------------------- 141 * 11.15.10 bge0 142 * 11.15.10 ce0 143 * 11.15.10 vnic1 144 * 11.15.15 bge0 145 * 11.15.15 ce0 146 * 11.15.15 vnic1 147 */ 148 net_time_entry_t *net_time_head; 149 net_time_entry_t *net_time_tail; 150 151 /* 152 * Optimization II : List sorted by resources 153 * Time Resource .. 154 * ------------------------------- 155 * 11.15.10 bge0 156 * 11.15.15 bge0 157 * 11.15.10 ce0 158 * 11.15.15 ce0 159 * 11.15.10 vnic1 160 * 11.15.15 vnic1 161 */ 162 net_time_entry_t *net_ctime_head; 163 net_time_entry_t *net_ctime_tail; 164 165 /* Common to both the above (sorted) lists. */ 166 int net_time_entries; 167 } net_table_t; 168 169 #define NET_DATE_GREATER 0 170 #define NET_DATE_LESSER 1 171 #define NET_DATE_EQUAL 2 172 173 #define NET_TIME_GREATER 0 174 #define NET_TIME_LESSER 1 175 #define NET_TIME_EQUAL 2 176 177 #ifndef _LP64 178 #define FMT_UINT64 "%-15llu" 179 #else 180 #define FMT_UINT64 "%-15lu" 181 #endif 182 183 /* 184 * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements. 185 */ 186 static void 187 dissect_time(char *tbuf, net_time_t *nt) 188 { 189 char *d; 190 char *t; 191 char *dd; 192 char *h; 193 char *endp; 194 195 if (tbuf == NULL || nt == NULL) 196 return; 197 198 d = strtok(tbuf, ","); /* Date */ 199 t = strtok(NULL, ","); /* Time */ 200 201 /* Month */ 202 dd = strtok(d, "/"); 203 if (dd == NULL) 204 return; 205 nt->net_time_mon = strtol(dd, &endp, 10); 206 207 /* Day */ 208 dd = strtok(NULL, "/"); 209 if (dd == NULL) 210 return; 211 nt->net_time_day = strtol(dd, &endp, 10); 212 213 /* Year */ 214 dd = strtok(NULL, "/"); 215 if (dd == NULL) 216 return; 217 nt->net_time_yr = strtol(dd, &endp, 10); 218 if (strlen(dd) <= 2) 219 nt->net_time_yr += 2000; 220 221 if (t == NULL) 222 return; 223 224 /* Hour */ 225 h = strtok(t, ":"); 226 if (h == NULL) 227 return; 228 nt->net_time_hr = strtol(h, &endp, 10); 229 230 /* Min */ 231 h = strtok(NULL, ":"); 232 if (h == NULL) 233 return; 234 nt->net_time_min = strtol(h, &endp, 10); 235 236 /* Sec */ 237 h = strtok(NULL, ":"); 238 if (h == NULL) 239 return; 240 nt->net_time_sec = strtol(h, &endp, 10); 241 } 242 243 /* Get a stat item from an object in the exacct file */ 244 static void 245 add_stat_item(ea_object_t *o, net_stat_t *ns) 246 { 247 switch (o->eo_catalog & EXT_TYPE_MASK) { 248 case EXT_STRING: 249 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) { 250 (void) strncpy(ns->net_stat_name, o->eo_item.ei_string, 251 strlen(o->eo_item.ei_string)); 252 } 253 break; 254 case EXT_UINT64: 255 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) { 256 time_t _time; 257 char timebuf[TIMEBUFLEN]; 258 259 ns->net_stat_ctime = o->eo_item.ei_uint64; 260 _time = ns->net_stat_ctime; 261 (void) strftime(timebuf, sizeof (timebuf), 262 "%m/%d/%Y,%T\n", localtime(&_time)); 263 dissect_time(timebuf, &ns->net_stat_time); 264 } else if ((o->eo_catalog & EXD_DATA_MASK) == 265 EXD_NET_STATS_IBYTES) { 266 ns->net_stat_ibytes = o->eo_item.ei_uint64; 267 } else if ((o->eo_catalog & EXD_DATA_MASK) == 268 EXD_NET_STATS_OBYTES) { 269 ns->net_stat_obytes = o->eo_item.ei_uint64; 270 } else if ((o->eo_catalog & EXD_DATA_MASK) == 271 EXD_NET_STATS_IPKTS) { 272 ns->net_stat_ipackets = o->eo_item.ei_uint64; 273 } else if ((o->eo_catalog & EXD_DATA_MASK) == 274 EXD_NET_STATS_OPKTS) { 275 ns->net_stat_opackets = o->eo_item.ei_uint64; 276 } else if ((o->eo_catalog & EXD_DATA_MASK) == 277 EXD_NET_STATS_IERRPKTS) { 278 ns->net_stat_ierrors = o->eo_item.ei_uint64; 279 } else if ((o->eo_catalog & EXD_DATA_MASK) == 280 EXD_NET_STATS_OERRPKTS) { 281 ns->net_stat_oerrors = o->eo_item.ei_uint64; 282 } 283 break; 284 default: 285 break; 286 } 287 } 288 289 /* Get a description item from an object in the exacct file */ 290 static void 291 add_desc_item(ea_object_t *o, net_desc_t *nd) 292 { 293 switch (o->eo_catalog & EXT_TYPE_MASK) { 294 case EXT_STRING: 295 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) { 296 (void) strncpy(nd->net_desc_name, o->eo_item.ei_string, 297 strlen(o->eo_item.ei_string)); 298 } else if ((o->eo_catalog & EXD_DATA_MASK) == 299 EXD_NET_DESC_DEVNAME) { 300 (void) strncpy(nd->net_desc_devname, 301 o->eo_item.ei_string, strlen(o->eo_item.ei_string)); 302 } 303 break; 304 case EXT_UINT8: 305 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) { 306 nd->net_desc_protocol = o->eo_item.ei_uint8; 307 } else if ((o->eo_catalog & EXD_DATA_MASK) == 308 EXD_NET_DESC_DSFIELD) { 309 nd->net_desc_dsfield = o->eo_item.ei_uint8; 310 } 311 break; 312 case EXT_UINT16: 313 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) { 314 nd->net_desc_sport = o->eo_item.ei_uint16; 315 } else if ((o->eo_catalog & EXD_DATA_MASK) == 316 EXD_NET_DESC_DPORT) { 317 nd->net_desc_dport = o->eo_item.ei_uint16; 318 } else if ((o->eo_catalog & EXD_DATA_MASK) == 319 EXD_NET_DESC_SAP) { 320 nd->net_desc_sap = o->eo_item.ei_uint16; 321 } else if ((o->eo_catalog & EXD_DATA_MASK) == 322 EXD_NET_DESC_VLAN_TPID) { 323 nd->net_desc_vlan_tpid = o->eo_item.ei_uint16; 324 } else if ((o->eo_catalog & EXD_DATA_MASK) == 325 EXD_NET_DESC_VLAN_TCI) { 326 nd->net_desc_vlan_tci = o->eo_item.ei_uint16; 327 } else if ((o->eo_catalog & EXD_DATA_MASK) == 328 EXD_NET_DESC_PRIORITY) { 329 nd->net_desc_priority = o->eo_item.ei_uint16; 330 } 331 break; 332 case EXT_UINT32: 333 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR || 334 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) { 335 struct in_addr addr; 336 337 addr.s_addr = htonl(o->eo_item.ei_uint32); 338 339 if ((o->eo_catalog & EXD_DATA_MASK) == 340 EXD_NET_DESC_V4SADDR) { 341 IN6_INADDR_TO_V4MAPPED(&addr, 342 &nd->net_desc_saddr); 343 } else { 344 IN6_INADDR_TO_V4MAPPED(&addr, 345 &nd->net_desc_daddr); 346 } 347 } 348 break; 349 case EXT_UINT64: 350 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT) 351 nd->net_desc_bw_limit = o->eo_item.ei_uint64; 352 break; 353 case EXT_RAW: 354 if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR || 355 (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) { 356 in6_addr_t addr; 357 358 addr = *(in6_addr_t *)o->eo_item.ei_raw; 359 if ((o->eo_catalog & EXD_DATA_MASK) == 360 EXD_NET_DESC_V6SADDR) { 361 nd->net_desc_saddr = addr; 362 } else { 363 nd->net_desc_daddr = addr; 364 } 365 } else if ((o->eo_catalog & EXD_DATA_MASK) == 366 EXD_NET_DESC_EHOST) { 367 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost, 368 ETHERADDRL); 369 } else if ((o->eo_catalog & EXD_DATA_MASK) == 370 EXD_NET_DESC_EDEST) { 371 bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest, 372 ETHERADDRL); 373 } 374 break; 375 default: 376 break; 377 } 378 } 379 380 /* Add a description item to the table */ 381 static dladm_status_t 382 add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd) 383 { 384 net_entry_t *ne; 385 386 if ((ne = calloc(1, sizeof (net_entry_t))) == NULL) 387 return (DLADM_STATUS_NOMEM); 388 389 if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) { 390 free(ne); 391 return (DLADM_STATUS_NOMEM); 392 } 393 394 ne->net_entry_desc = nd; 395 ne->net_entry_shead = NULL; 396 ne->net_entry_stail = NULL; 397 ne->net_entry_scount = 0; 398 399 if (net_table->net_table_head == NULL) { 400 net_table->net_table_head = ne; 401 net_table->net_table_tail = ne; 402 } else { 403 net_table->net_table_tail->net_entry_next = ne; 404 net_table->net_table_tail = ne; 405 } 406 net_table->net_entries++; 407 return (DLADM_STATUS_OK); 408 } 409 410 /* Compare dates and return if t1 is equal, greater or lesser than t2 */ 411 static int 412 compare_date(net_time_t *t1, net_time_t *t2) 413 { 414 if (t1->net_time_yr == t2->net_time_yr && 415 t1->net_time_mon == t2->net_time_mon && 416 t1->net_time_day == t2->net_time_day) { 417 return (NET_DATE_EQUAL); 418 } 419 if (t1->net_time_yr > t2->net_time_yr || 420 (t1->net_time_yr == t2->net_time_yr && 421 t1->net_time_mon > t2->net_time_mon) || 422 (t1->net_time_yr == t2->net_time_yr && 423 t1->net_time_mon == t2->net_time_mon && 424 t1->net_time_day > t2->net_time_day)) { 425 return (NET_DATE_GREATER); 426 } 427 return (NET_DATE_LESSER); 428 } 429 430 /* Compare times and return if t1 is equal, greater or lesser than t2 */ 431 static int 432 compare_time(net_time_t *t1, net_time_t *t2) 433 { 434 int cd; 435 436 cd = compare_date(t1, t2); 437 438 if (cd == NET_DATE_GREATER) { 439 return (NET_TIME_GREATER); 440 } else if (cd == NET_DATE_LESSER) { 441 return (NET_TIME_LESSER); 442 } else { 443 if (t1->net_time_hr == t2->net_time_hr && 444 t1->net_time_min == t2->net_time_min && 445 t1->net_time_sec == t2->net_time_sec) { 446 return (NET_TIME_EQUAL); 447 } 448 if (t1->net_time_hr > t2->net_time_hr || 449 (t1->net_time_hr == t2->net_time_hr && 450 t1->net_time_min > t2->net_time_min) || 451 (t1->net_time_hr == t2->net_time_hr && 452 t1->net_time_min == t2->net_time_min && 453 t1->net_time_sec > t2->net_time_sec)) { 454 return (NET_TIME_GREATER); 455 } 456 } 457 return (NET_TIME_LESSER); 458 } 459 460 /* 461 * Given a start and end time and start and end entries check if the 462 * times are within the range, and adjust, if needed. 463 */ 464 static dladm_status_t 465 chk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns, 466 net_time_t *ens) 467 { 468 if (s != NULL && e != NULL) { 469 if (compare_time(s, e) == NET_TIME_GREATER) 470 return (DLADM_STATUS_BADTIMEVAL); 471 } 472 if (s != NULL) { 473 if (compare_time(s, sns) == NET_TIME_LESSER) { 474 s->net_time_yr = sns->net_time_yr; 475 s->net_time_mon = sns->net_time_mon; 476 s->net_time_day = sns->net_time_day; 477 s->net_time_hr = sns->net_time_hr; 478 s->net_time_min = sns->net_time_min; 479 s->net_time_sec = sns->net_time_sec; 480 } 481 } 482 if (e != NULL) { 483 if (compare_time(e, ens) == NET_TIME_GREATER) { 484 e->net_time_yr = ens->net_time_yr; 485 e->net_time_mon = ens->net_time_mon; 486 e->net_time_day = ens->net_time_day; 487 e->net_time_hr = ens->net_time_hr; 488 e->net_time_min = ens->net_time_min; 489 e->net_time_sec = ens->net_time_sec; 490 } 491 } 492 return (DLADM_STATUS_OK); 493 } 494 495 /* 496 * Given a start and end time (strings), convert them into net_time_t 497 * and also check for the range given the head and tail of the list. 498 * If stime is lower then head or etime is greated than tail, adjust. 499 */ 500 static dladm_status_t 501 get_time_range(net_time_entry_t *head, net_time_entry_t *tail, 502 net_time_t *st, net_time_t *et, char *stime, char *etime) 503 { 504 bzero(st, sizeof (net_time_t)); 505 bzero(et, sizeof (net_time_t)); 506 507 if (stime == NULL && etime == NULL) 508 return (0); 509 510 if (stime != NULL) 511 dissect_time(stime, st); 512 if (etime != NULL) 513 dissect_time(etime, et); 514 515 if (stime != NULL || etime != NULL) { 516 return (chk_time_bound(stime == NULL ? NULL : st, 517 etime == NULL ? NULL : et, 518 &head->my_time_stat->net_stat_time, 519 &tail->my_time_stat->net_stat_time)); 520 } 521 return (0); 522 } 523 524 /* 525 * Walk the list from a given starting point and return when we find 526 * an entry that is greater or equal to st. lasttime will point to the 527 * previous time entry. 528 */ 529 static void 530 get_starting_point(net_time_entry_t *head, net_time_entry_t **start, 531 net_time_t *st, char *stime, uint64_t *lasttime) 532 { 533 net_time_entry_t *next = head; 534 535 if (head == NULL) { 536 *start = NULL; 537 return; 538 } 539 if (stime == NULL) { 540 *start = head; 541 *lasttime = head->my_time_stat->net_stat_ctime; 542 return; 543 } 544 *start = NULL; 545 while (next != NULL) { 546 if (compare_time(st, 547 &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) { 548 *lasttime = next->my_time_stat->net_stat_ctime; 549 next = next->net_time_entry_next; 550 continue; 551 } 552 *start = next; 553 break; 554 } 555 } 556 557 /* 558 * Point entry (pe) functions 559 */ 560 /* Clear all the counters. Done after the contents are written to the file */ 561 static void 562 clear_pe(net_plot_entry_t *pe, int entries, int *pentries) 563 { 564 int count; 565 566 for (count = 0; count < entries; count++) { 567 pe[count].net_pe_totbytes = 0; 568 pe[count].net_pe_totibytes = 0; 569 pe[count].net_pe_totobytes = 0; 570 pe[count].net_pe_tottime = 0; 571 } 572 *pentries = 0; 573 } 574 575 /* Update an entry in the point entry table */ 576 static void 577 update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries, 578 int *pentries, uint64_t lasttime) 579 { 580 int count; 581 582 for (count = 0; count < nentries; count++) { 583 if ((strlen(nns->net_stat_name) == 584 strlen(pe[count].net_pe_name)) && 585 (strncmp(pe[count].net_pe_name, nns->net_stat_name, 586 strlen(nns->net_stat_name)) == 0)) { 587 break; 588 } 589 } 590 if (count == nentries) 591 return; 592 593 if (pe[count].net_pe_totbytes == 0) 594 pe[count].net_pe_lasttime = lasttime; 595 596 pe[count].net_pe_totbytes += nns->net_stat_ibytes + 597 nns->net_stat_obytes; 598 pe[count].net_pe_tottime += nns->net_stat_tdiff; 599 pe[count].net_pe_totibytes += nns->net_stat_ibytes; 600 pe[count].net_pe_totobytes += nns->net_stat_obytes; 601 (*pentries)++; 602 } 603 604 /* Flush the contents of the point entry table to the file. */ 605 static void 606 add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe, 607 net_stat_t *ns, int entries, void *arg) 608 { 609 int count; 610 dladm_usage_t usage; 611 uint64_t tottime; 612 613 bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime)); 614 for (count = 0; count < entries; count++) { 615 bcopy(pe[count].net_pe_name, &usage.du_name, 616 sizeof (usage.du_name)); 617 bcopy(&pe[count].net_pe_lasttime, &usage.du_stime, 618 sizeof (usage.du_stime)); 619 usage.du_rbytes = pe[count].net_pe_totibytes; 620 usage.du_obytes = pe[count].net_pe_totobytes; 621 tottime = pe[count].net_pe_tottime; 622 usage.du_bandwidth = (tottime > 0) ? 623 ((pe[count].net_pe_totbytes * 8) / tottime) : 0; 624 usage.du_last = (count == entries-1); 625 fn(&usage, arg); 626 } 627 } 628 629 /* 630 * Net entry functions 631 */ 632 static net_entry_t * 633 get_ne_from_table(net_table_t *net_table, char *name) 634 { 635 int count; 636 net_desc_t *nd; 637 net_entry_t *ne = net_table->net_table_head; 638 639 for (count = 0; count < net_table->net_entries; count++) { 640 nd = ne->net_entry_desc; 641 if ((strlen(name) == strlen(nd->net_desc_name)) && 642 (strncmp(name, nd->net_desc_name, strlen(name)) == 0)) { 643 return (ne); 644 } 645 ne = ne->net_entry_next; 646 } 647 return (NULL); 648 } 649 650 /* Get the entry for the descriptor, if it exists */ 651 static net_desc_t * 652 get_ndesc(net_table_t *net_table, net_desc_t *nd) 653 { 654 int count; 655 net_desc_t *nd1; 656 net_entry_t *ne = net_table->net_table_head; 657 658 for (count = 0; count < net_table->net_entries; count++) { 659 nd1 = ne->net_entry_desc; 660 if (strlen(nd1->net_desc_name) == strlen(nd->net_desc_name) && 661 strlen(nd1->net_desc_devname) == 662 strlen(nd->net_desc_devname) && 663 strncmp(nd1->net_desc_name, nd->net_desc_name, 664 strlen(nd1->net_desc_name)) == 0 && 665 strncmp(nd1->net_desc_devname, nd->net_desc_devname, 666 strlen(nd1->net_desc_devname)) == 0 && 667 bcmp(nd1->net_desc_ehost, nd->net_desc_ehost, 668 ETHERADDRL) == 0 && 669 bcmp(nd1->net_desc_edest, nd->net_desc_edest, 670 ETHERADDRL) == 0 && 671 nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid && 672 nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci && 673 nd1->net_desc_sap == nd->net_desc_sap && 674 nd1->net_desc_cpuid == nd->net_desc_cpuid && 675 nd1->net_desc_priority == nd->net_desc_priority && 676 nd1->net_desc_bw_limit == nd->net_desc_bw_limit && 677 nd1->net_desc_sport == nd->net_desc_sport && 678 nd1->net_desc_dport == nd->net_desc_dport && 679 nd1->net_desc_protocol == nd->net_desc_protocol && 680 nd1->net_desc_dsfield == nd->net_desc_dsfield && 681 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr, 682 &nd->net_desc_saddr) && 683 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr, 684 &nd->net_desc_daddr)) { 685 return (nd1); 686 } 687 ne = ne->net_entry_next; 688 } 689 return (NULL); 690 } 691 692 /* 693 * Update the stat entries. The stats in the file are cumulative, so in order 694 * to have increments, we maintain a reference stat entry, which contains 695 * the stats when the record was first written and a total stat entry, which 696 * maintains the running count. When we want to add a stat entry, if it 697 * the reference stat entry, we don't come here. For subsequent entries, 698 * we get the increment by subtracting the current value from the reference 699 * stat and the total stat. 700 */ 701 static void 702 update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref) 703 { 704 705 /* get the increment */ 706 ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes); 707 ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes); 708 ns1->net_stat_ipackets -= (ref->net_stat_ipackets + 709 ref->net_stat_tipackets); 710 ns1->net_stat_opackets -= (ref->net_stat_opackets + 711 ref->net_stat_topackets); 712 ns1->net_stat_ierrors -= (ref->net_stat_ierrors + 713 ref->net_stat_tierrors); 714 ns1->net_stat_oerrors -= (ref->net_stat_oerrors + 715 ref->net_stat_toerrors); 716 717 /* update total bytes */ 718 ref->net_stat_tibytes += ns1->net_stat_ibytes; 719 ref->net_stat_tobytes += ns1->net_stat_obytes; 720 ref->net_stat_tipackets += ns1->net_stat_ipackets; 721 ref->net_stat_topackets += ns1->net_stat_opackets; 722 ref->net_stat_tierrors += ns1->net_stat_ierrors; 723 ref->net_stat_toerrors += ns1->net_stat_oerrors; 724 725 ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes; 726 ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes; 727 ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets; 728 ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets; 729 ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors; 730 ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors; 731 } 732 733 /* Add the stat entry into the table */ 734 static dladm_status_t 735 add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns) 736 { 737 net_entry_t *ne; 738 739 ne = get_ne_from_table(net_table, ns->net_stat_name); 740 if (ne == NULL) 741 return (DLADM_STATUS_NOMEM); 742 743 /* Ptr to flow desc */ 744 ns->net_stat_desc = ne->net_entry_desc; 745 if (ns->net_stat_desc->net_desc_newrec) { 746 ns->net_stat_desc->net_desc_newrec = B_FALSE; 747 ns->net_stat_isref = B_TRUE; 748 ne->net_entry_sref = ns; 749 } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes || 750 (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) { 751 ns->net_stat_isref = B_TRUE; 752 ne->net_entry_sref = ns; 753 } else { 754 ns->net_stat_isref = B_FALSE; 755 update_stats(ns, ne, ne->net_entry_sref); 756 } 757 if (ne->net_entry_shead == NULL) { 758 ne->net_entry_shead = ns; 759 ne->net_entry_stail = ns; 760 } else { 761 if (!ns->net_stat_isref) { 762 ne->net_entry_ttime += (ns->net_stat_ctime - 763 ne->net_entry_stail->net_stat_ctime); 764 ns->net_stat_tdiff = ns->net_stat_ctime - 765 ne->net_entry_stail->net_stat_ctime; 766 } 767 ne->net_entry_stail->net_stat_next = ns; 768 ne->net_entry_stail = ns; 769 } 770 771 ne->net_entry_scount++; 772 return (DLADM_STATUS_OK); 773 } 774 775 /* Add a flow/link descriptor record to the table */ 776 static dladm_status_t 777 add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs) 778 { 779 net_desc_t *nd; 780 net_desc_t *dnd; 781 int count; 782 ea_object_t scratch; 783 784 if ((nd = calloc(1, sizeof (net_desc_t))) == NULL) 785 return (DLADM_STATUS_NOMEM); 786 nd->net_desc_newrec = B_TRUE; 787 788 for (count = 0; count < nobjs; count++) { 789 if (ea_get_object(ef, &scratch) == -1) { 790 free(nd); 791 return (DLADM_STATUS_NOMEM); 792 } 793 add_desc_item(&scratch, nd); 794 } 795 if ((dnd = get_ndesc(net_table, nd)) != NULL) { 796 dnd->net_desc_newrec = B_TRUE; 797 free(nd); 798 return (DLADM_STATUS_OK); 799 } 800 if (add_desc_to_tbl(net_table, nd) != 0) { 801 free(nd); 802 return (DLADM_STATUS_NOMEM); 803 } 804 return (DLADM_STATUS_OK); 805 } 806 807 /* Make an entry into the time sorted list */ 808 static void 809 addto_time_list(net_table_t *net_table, net_time_entry_t *nt, 810 net_time_entry_t *ntc) 811 { 812 net_stat_t *ns = nt->my_time_stat; 813 net_stat_t *ns1; 814 net_time_entry_t *end; 815 net_time_t *t1; 816 int count; 817 818 t1 = &ns->net_stat_time; 819 820 net_table->net_time_entries++; 821 822 if (net_table->net_time_head == NULL) { 823 net_table->net_time_head = nt; 824 net_table->net_time_tail = nt; 825 } else { 826 net_table->net_time_tail->net_time_entry_next = nt; 827 nt->net_time_entry_prev = net_table->net_time_tail; 828 net_table->net_time_tail = nt; 829 } 830 831 if (net_table->net_ctime_head == NULL) { 832 net_table->net_ctime_head = ntc; 833 net_table->net_ctime_tail = ntc; 834 } else { 835 end = net_table->net_ctime_tail; 836 count = 0; 837 while (count < net_table->net_time_entries - 1) { 838 ns1 = end->my_time_stat; 839 /* Just add it to the tail */ 840 if (compare_date(t1, &ns1->net_stat_time) == 841 NET_DATE_GREATER) { 842 break; 843 } 844 if ((strlen(ns1->net_stat_name) == 845 strlen(ns->net_stat_name)) && 846 (strncmp(ns1->net_stat_name, ns->net_stat_name, 847 strlen(ns1->net_stat_name)) == 0)) { 848 ntc->net_time_entry_next = 849 end->net_time_entry_next; 850 if (end->net_time_entry_next != NULL) { 851 end->net_time_entry_next-> 852 net_time_entry_prev = ntc; 853 } else { 854 net_table->net_ctime_tail = ntc; 855 } 856 end->net_time_entry_next = ntc; 857 ntc->net_time_entry_prev = end; 858 return; 859 } 860 count++; 861 end = end->net_time_entry_prev; 862 } 863 net_table->net_ctime_tail->net_time_entry_next = ntc; 864 ntc->net_time_entry_prev = net_table->net_ctime_tail; 865 net_table->net_ctime_tail = ntc; 866 } 867 } 868 869 /* Add stat entry into the lists */ 870 static dladm_status_t 871 add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs) 872 { 873 net_stat_t *ns; 874 int count; 875 ea_object_t scratch; 876 net_time_entry_t *nt; 877 net_time_entry_t *ntc; 878 879 if ((ns = calloc(1, sizeof (net_stat_t))) == NULL) 880 return (DLADM_STATUS_NOMEM); 881 882 if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) { 883 free(ns); 884 return (DLADM_STATUS_NOMEM); 885 } 886 if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) { 887 free(ns); 888 free(nt); 889 return (DLADM_STATUS_NOMEM); 890 } 891 892 nt->my_time_stat = ns; 893 ntc->my_time_stat = ns; 894 895 for (count = 0; count < nobjs; count++) { 896 if (ea_get_object(ef, &scratch) == -1) { 897 free(ns); 898 free(nt); 899 free(ntc); 900 return (DLADM_STATUS_NOMEM); 901 } 902 add_stat_item(&scratch, ns); 903 } 904 if (add_stat_to_tbl(net_table, ns) != 0) { 905 free(ns); 906 free(nt); 907 free(ntc); 908 return (DLADM_STATUS_NOMEM); 909 } 910 addto_time_list(net_table, nt, ntc); 911 return (DLADM_STATUS_OK); 912 } 913 914 /* Free the entire table */ 915 static void 916 free_logtable(net_table_t *net_table) 917 { 918 net_entry_t *head; 919 net_entry_t *next; 920 net_stat_t *ns; 921 net_stat_t *ns1; 922 net_time_entry_t *thead; 923 net_time_entry_t *tnext; 924 925 thead = net_table->net_time_head; 926 while (thead != NULL) { 927 thead->my_time_stat = NULL; 928 tnext = thead->net_time_entry_next; 929 thead->net_time_entry_next = NULL; 930 thead->net_time_entry_prev = NULL; 931 free(thead); 932 thead = tnext; 933 } 934 net_table->net_time_head = NULL; 935 net_table->net_time_tail = NULL; 936 937 thead = net_table->net_ctime_head; 938 while (thead != NULL) { 939 thead->my_time_stat = NULL; 940 tnext = thead->net_time_entry_next; 941 thead->net_time_entry_next = NULL; 942 thead->net_time_entry_prev = NULL; 943 free(thead); 944 thead = tnext; 945 } 946 net_table->net_ctime_head = NULL; 947 net_table->net_ctime_tail = NULL; 948 949 net_table->net_time_entries = 0; 950 951 head = net_table->net_table_head; 952 while (head != NULL) { 953 next = head->net_entry_next; 954 head->net_entry_next = NULL; 955 ns = head->net_entry_shead; 956 while (ns != NULL) { 957 ns1 = ns->net_stat_next; 958 free(ns); 959 ns = ns1; 960 } 961 head->net_entry_scount = 0; 962 head->net_entry_sref = NULL; 963 free(head->net_entry_desc); 964 free(head->net_entry_tstats); 965 free(head); 966 head = next; 967 } 968 net_table->net_table_head = NULL; 969 net_table->net_table_tail = NULL; 970 net_table->net_time_entries = 0; 971 free(net_table); 972 } 973 974 /* Parse the exacct file, and return the parsed table. */ 975 static void * 976 parse_logfile(char *file, int logtype, dladm_status_t *status) 977 { 978 ea_file_t ef; 979 ea_object_t scratch; 980 net_table_t *net_table; 981 982 *status = DLADM_STATUS_OK; 983 if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) { 984 *status = DLADM_STATUS_NOMEM; 985 return (NULL); 986 } 987 if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) { 988 *status = DLADM_STATUS_BADARG; 989 free(net_table); 990 return (NULL); 991 } 992 bzero(&scratch, sizeof (ea_object_t)); 993 while (ea_get_object(&ef, &scratch) != -1) { 994 if (scratch.eo_type != EO_GROUP) { 995 (void) ea_free_item(&scratch, EUP_ALLOC); 996 (void) bzero(&scratch, sizeof (ea_object_t)); 997 continue; 998 } 999 /* Read Link Desc/Stat records */ 1000 if (logtype == DLADM_LOGTYPE_FLOW) { 1001 /* Flow Descriptor */ 1002 if ((scratch.eo_catalog & 1003 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) { 1004 (void) add_desc(net_table, &ef, 1005 scratch.eo_group.eg_nobjs - 1); 1006 /* Flow Stats */ 1007 } else if ((scratch.eo_catalog & 1008 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) { 1009 (void) add_stats(net_table, &ef, 1010 scratch.eo_group.eg_nobjs - 1); 1011 } 1012 } else if (logtype == DLADM_LOGTYPE_LINK) { 1013 /* Link Descriptor */ 1014 if ((scratch.eo_catalog & 1015 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) { 1016 (void) add_desc(net_table, &ef, 1017 scratch.eo_group.eg_nobjs - 1); 1018 /* Link Stats */ 1019 } else if ((scratch.eo_catalog & 1020 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) { 1021 (void) add_stats(net_table, &ef, 1022 scratch.eo_group.eg_nobjs - 1); 1023 } 1024 } else { 1025 if (((scratch.eo_catalog & EXD_DATA_MASK) == 1026 EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog & 1027 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) { 1028 (void) add_desc(net_table, &ef, 1029 scratch.eo_group.eg_nobjs - 1); 1030 } else if (((scratch.eo_catalog & EXD_DATA_MASK) == 1031 EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog & 1032 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) { 1033 (void) add_stats(net_table, &ef, 1034 scratch.eo_group.eg_nobjs - 1); 1035 } 1036 } 1037 (void) ea_free_item(&scratch, EUP_ALLOC); 1038 (void) bzero(&scratch, sizeof (ea_object_t)); 1039 } 1040 1041 (void) ea_close(&ef); 1042 return ((void *)net_table); 1043 } 1044 1045 /* 1046 * Walk the ctime list. This is used when looking for usage records 1047 * based on a "resource" name. 1048 */ 1049 dladm_status_t 1050 dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype, 1051 char *logfile, char *resource, char *stime, char *etime, void *arg) 1052 { 1053 net_table_t *net_table; 1054 net_time_t st, et; 1055 net_time_entry_t *start; 1056 net_stat_t *ns = NULL; 1057 net_stat_t *nns; 1058 uint64_t tot_time = 0; 1059 uint64_t last_time; 1060 uint64_t tot_bytes = 0; 1061 uint64_t tot_ibytes = 0; 1062 uint64_t tot_obytes = 0; 1063 boolean_t gotstart = B_FALSE; 1064 dladm_status_t status; 1065 dladm_usage_t usage; 1066 int step = 1; 1067 1068 /* Parse the log file */ 1069 net_table = parse_logfile(logfile, logtype, &status); 1070 if (net_table == NULL) 1071 return (status); 1072 1073 if (net_table->net_entries == 0) 1074 return (DLADM_STATUS_OK); 1075 start = net_table->net_ctime_head; 1076 1077 /* Time range */ 1078 status = get_time_range(net_table->net_ctime_head, 1079 net_table->net_ctime_tail, &st, &et, stime, etime); 1080 if (status != DLADM_STATUS_OK) 1081 return (status); 1082 1083 while (start != NULL) { 1084 nns = start->my_time_stat; 1085 1086 /* Get to the resource we are interested in */ 1087 if ((strlen(resource) != strlen(nns->net_stat_name)) || 1088 (strncmp(resource, nns->net_stat_name, 1089 strlen(nns->net_stat_name)) != 0)) { 1090 start = start->net_time_entry_next; 1091 continue; 1092 } 1093 1094 /* Find the first record */ 1095 if (!gotstart) { 1096 get_starting_point(start, &start, &st, stime, 1097 &last_time); 1098 if (start == NULL) 1099 break; 1100 nns = start->my_time_stat; 1101 gotstart = B_TRUE; 1102 } 1103 1104 /* Write one entry and return if we are out of the range */ 1105 if (etime != NULL && compare_time(&nns->net_stat_time, &et) 1106 == NET_TIME_GREATER) { 1107 if (tot_bytes != 0) { 1108 bcopy(ns->net_stat_name, &usage.du_name, 1109 sizeof (usage.du_name)); 1110 bcopy(&last_time, &usage.du_stime, 1111 sizeof (usage.du_stime)); 1112 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1113 sizeof (usage.du_etime)); 1114 usage.du_rbytes = tot_ibytes; 1115 usage.du_obytes = tot_obytes; 1116 usage.du_bandwidth = tot_bytes*8/tot_time; 1117 usage.du_last = B_TRUE; 1118 fn(&usage, arg); 1119 } 1120 return (DLADM_STATUS_OK); 1121 } 1122 1123 /* 1124 * If this is a reference entry, just print what we have 1125 * and proceed. 1126 */ 1127 if (nns->net_stat_isref) { 1128 if (tot_bytes != 0) { 1129 bcopy(&nns->net_stat_name, &usage.du_name, 1130 sizeof (usage.du_name)); 1131 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1132 sizeof (usage.du_stime)); 1133 usage.du_rbytes = tot_ibytes; 1134 usage.du_obytes = tot_obytes; 1135 usage.du_bandwidth = tot_bytes*8/tot_time; 1136 usage.du_last = B_TRUE; 1137 fn(&usage, arg); 1138 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1139 tot_obytes, step); 1140 } 1141 last_time = nns->net_stat_ctime; 1142 start = start->net_time_entry_next; 1143 continue; 1144 } 1145 1146 ns = nns; 1147 if (--step == 0) { 1148 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1149 tot_ibytes += ns->net_stat_ibytes; 1150 tot_obytes += ns->net_stat_obytes; 1151 tot_time += ns->net_stat_tdiff; 1152 bcopy(&ns->net_stat_name, &usage.du_name, 1153 sizeof (usage.du_name)); 1154 bcopy(&last_time, &usage.du_stime, 1155 sizeof (usage.du_stime)); 1156 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1157 sizeof (usage.du_etime)); 1158 usage.du_rbytes = tot_ibytes; 1159 usage.du_obytes = tot_obytes; 1160 usage.du_bandwidth = tot_bytes*8/tot_time; 1161 usage.du_last = B_TRUE; 1162 fn(&usage, arg); 1163 1164 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1165 tot_obytes, step); 1166 last_time = ns->net_stat_ctime; 1167 } else { 1168 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1169 tot_ibytes += ns->net_stat_ibytes; 1170 tot_obytes += ns->net_stat_obytes; 1171 tot_time += ns->net_stat_tdiff; 1172 } 1173 start = start->net_time_entry_next; 1174 } 1175 1176 if (tot_bytes != 0) { 1177 bcopy(&ns->net_stat_name, &usage.du_name, 1178 sizeof (usage.du_name)); 1179 bcopy(&last_time, &usage.du_stime, 1180 sizeof (usage.du_stime)); 1181 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1182 sizeof (usage.du_etime)); 1183 usage.du_rbytes = tot_ibytes; 1184 usage.du_obytes = tot_obytes; 1185 usage.du_bandwidth = tot_bytes*8/tot_time; 1186 usage.du_last = B_TRUE; 1187 fn(&usage, arg); 1188 } 1189 1190 free_logtable(net_table); 1191 return (status); 1192 } 1193 1194 /* 1195 * Walk the time sorted list if a resource is not specified. 1196 */ 1197 dladm_status_t 1198 dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype, 1199 char *logfile, char *stime, char *etime, void *arg) 1200 { 1201 net_table_t *net_table; 1202 net_time_entry_t *start; 1203 net_stat_t *ns = NULL, *nns; 1204 net_time_t st, et, *t1; 1205 net_desc_t *nd; 1206 net_entry_t *ne; 1207 net_plot_entry_t *pe; 1208 int count; 1209 int step = 1; 1210 int nentries = 0, pentries = 0; 1211 uint64_t last_time; 1212 dladm_status_t status; 1213 1214 /* Parse the log file */ 1215 net_table = parse_logfile(logfile, logtype, &status); 1216 if (net_table == NULL) 1217 return (status); 1218 1219 if (net_table->net_entries == 0) 1220 return (DLADM_STATUS_OK); 1221 start = net_table->net_time_head; 1222 1223 /* Find the first and last records and starting point */ 1224 status = get_time_range(net_table->net_time_head, 1225 net_table->net_time_tail, &st, &et, stime, etime); 1226 if (status != DLADM_STATUS_OK) 1227 return (status); 1228 get_starting_point(start, &start, &st, stime, &last_time); 1229 /* 1230 * Could assert to be non-null, since get_time_range() 1231 * would have adjusted. 1232 */ 1233 if (start == NULL) 1234 return (DLADM_STATUS_BADTIMEVAL); 1235 1236 /* 1237 * Collect entries for all resources in a time slot before 1238 * writing to the file. 1239 */ 1240 nentries = net_table->net_entries; 1241 1242 pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1); 1243 if (pe == NULL) 1244 return (DLADM_STATUS_NOMEM); 1245 1246 ne = net_table->net_table_head; 1247 for (count = 0; count < nentries; count++) { 1248 nd = ne->net_entry_desc; 1249 pe[count].net_pe_name = nd->net_desc_name; 1250 ne = ne->net_entry_next; 1251 } 1252 1253 clear_pe(pe, nentries, &pentries); 1254 1255 /* Write header to file */ 1256 /* add_pe_to_file(fn, pe, ns, nentries, arg); */ 1257 1258 t1 = &start->my_time_stat->net_stat_time; 1259 1260 while (start != NULL) { 1261 1262 nns = start->my_time_stat; 1263 /* 1264 * We have crossed the time boundary, check if we need to 1265 * print out now. 1266 */ 1267 if (compare_time(&nns->net_stat_time, t1) == 1268 NET_TIME_GREATER) { 1269 /* return if we are out of the range */ 1270 if (etime != NULL && 1271 compare_time(&nns->net_stat_time, &et) == 1272 NET_TIME_GREATER) { 1273 if (pentries > 0) { 1274 add_pe_to_file(fn, pe, ns, nentries, 1275 arg); 1276 clear_pe(pe, nentries, &pentries); 1277 } 1278 free(pe); 1279 return (DLADM_STATUS_OK); 1280 } 1281 /* update the stats from the ns. */ 1282 t1 = &nns->net_stat_time; 1283 last_time = ns->net_stat_ctime; 1284 if (--step == 0) { 1285 if (pentries > 0) { 1286 add_pe_to_file(fn, pe, ns, nentries, 1287 arg); 1288 clear_pe(pe, nentries, &pentries); 1289 } 1290 step = 1; 1291 } 1292 } 1293 1294 /* 1295 * if this is a reference entry, just print what we have 1296 * for this resource and proceed. We will end up writing 1297 * the stats for all the entries when we hit a ref element, 1298 * which means 'steps' for some might not be accurate, but 1299 * that is fine, the alternative is to write only the 1300 * resource for which we hit a reference entry. 1301 */ 1302 if (nns->net_stat_isref) { 1303 if (pentries > 0) { 1304 add_pe_to_file(fn, pe, ns, nentries, arg); 1305 clear_pe(pe, nentries, &pentries); 1306 } 1307 step = 1; 1308 } else { 1309 update_pe(pe, nns, nentries, &pentries, last_time); 1310 } 1311 ns = nns; 1312 start = start->net_time_entry_next; 1313 } 1314 1315 if (pentries > 0) 1316 add_pe_to_file(fn, pe, ns, nentries, arg); 1317 1318 free(pe); 1319 free_logtable(net_table); 1320 1321 return (DLADM_STATUS_OK); 1322 } 1323 1324 dladm_status_t 1325 dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype, 1326 char *logfile, void *arg) 1327 { 1328 net_table_t *net_table; 1329 net_entry_t *ne; 1330 net_desc_t *nd; 1331 net_stat_t *ns; 1332 int count; 1333 dladm_usage_t usage; 1334 dladm_status_t status; 1335 1336 /* Parse the log file */ 1337 net_table = parse_logfile(logfile, logtype, &status); 1338 if (net_table == NULL) 1339 return (status); 1340 1341 if (net_table->net_entries == 0) 1342 return (DLADM_STATUS_OK); 1343 1344 ne = net_table->net_table_head; 1345 for (count = 0; count < net_table->net_entries; count++) { 1346 ns = ne->net_entry_tstats; 1347 nd = ne->net_entry_desc; 1348 1349 if (ns->net_stat_ibytes + ns->net_stat_obytes == 0) 1350 continue; 1351 bcopy(&nd->net_desc_name, &usage.du_name, 1352 sizeof (usage.du_name)); 1353 usage.du_duration = ne->net_entry_ttime; 1354 usage.du_ipackets = ns->net_stat_ipackets; 1355 usage.du_rbytes = ns->net_stat_ibytes; 1356 usage.du_opackets = ns->net_stat_opackets; 1357 usage.du_obytes = ns->net_stat_obytes; 1358 usage.du_bandwidth = 1359 (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 / 1360 usage.du_duration; 1361 usage.du_last = (count == net_table->net_entries-1); 1362 fn(&usage, arg); 1363 1364 ne = ne->net_entry_next; 1365 } 1366 1367 free_logtable(net_table); 1368 return (DLADM_STATUS_OK); 1369 } 1370 1371 /* 1372 * Walk the ctime list and display the dates of the records. 1373 */ 1374 dladm_status_t 1375 dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype, 1376 char *logfile, char *resource, void *arg) 1377 { 1378 net_table_t *net_table; 1379 net_time_entry_t *start; 1380 net_stat_t *nns; 1381 net_time_t st; 1382 net_time_t *lasttime = NULL; 1383 uint64_t last_time; 1384 boolean_t gotstart = B_FALSE; 1385 dladm_status_t status; 1386 dladm_usage_t usage; 1387 1388 /* Parse the log file */ 1389 net_table = parse_logfile(logfile, logtype, &status); 1390 if (net_table == NULL) 1391 return (status); 1392 1393 if (net_table->net_entries == 0) 1394 return (DLADM_STATUS_OK); 1395 1396 start = net_table->net_ctime_head; 1397 1398 while (start != NULL) { 1399 nns = start->my_time_stat; 1400 1401 /* get to the resource we are interested in */ 1402 if (resource != NULL) { 1403 if ((strlen(resource) != strlen(nns->net_stat_name)) || 1404 (strncmp(resource, nns->net_stat_name, 1405 strlen(nns->net_stat_name)) != 0)) { 1406 start = start->net_time_entry_next; 1407 continue; 1408 } 1409 } 1410 1411 /* get the starting point in the logfile */ 1412 if (!gotstart) { 1413 get_starting_point(start, &start, &st, NULL, 1414 &last_time); 1415 if (start == NULL) 1416 break; 1417 nns = start->my_time_stat; 1418 gotstart = B_TRUE; 1419 } 1420 1421 if (lasttime == NULL || 1422 compare_date(&nns->net_stat_time, lasttime) == 1423 NET_DATE_GREATER) { 1424 bzero(&usage, sizeof (dladm_usage_t)); 1425 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1426 sizeof (usage.du_stime)); 1427 fn(&usage, arg); 1428 lasttime = &nns->net_stat_time; 1429 } 1430 1431 start = start->net_time_entry_next; 1432 continue; 1433 } 1434 1435 free_logtable(net_table); 1436 return (status); 1437 } 1438