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 2009 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 (strcmp(pe[count].net_pe_name, nns->net_stat_name) == 0) 584 break; 585 } 586 if (count == nentries) 587 return; 588 589 if (pe[count].net_pe_totbytes == 0) 590 pe[count].net_pe_lasttime = lasttime; 591 592 pe[count].net_pe_totbytes += nns->net_stat_ibytes + 593 nns->net_stat_obytes; 594 pe[count].net_pe_tottime += nns->net_stat_tdiff; 595 pe[count].net_pe_totibytes += nns->net_stat_ibytes; 596 pe[count].net_pe_totobytes += nns->net_stat_obytes; 597 (*pentries)++; 598 } 599 600 /* Flush the contents of the point entry table to the file. */ 601 static void 602 add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe, 603 net_stat_t *ns, int entries, void *arg) 604 { 605 int count; 606 dladm_usage_t usage; 607 uint64_t tottime; 608 609 bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime)); 610 for (count = 0; count < entries; count++) { 611 bcopy(pe[count].net_pe_name, &usage.du_name, 612 sizeof (usage.du_name)); 613 bcopy(&pe[count].net_pe_lasttime, &usage.du_stime, 614 sizeof (usage.du_stime)); 615 usage.du_rbytes = pe[count].net_pe_totibytes; 616 usage.du_obytes = pe[count].net_pe_totobytes; 617 tottime = pe[count].net_pe_tottime; 618 usage.du_bandwidth = (tottime > 0) ? 619 ((pe[count].net_pe_totbytes * 8) / tottime) : 0; 620 usage.du_last = (count == entries-1); 621 fn(&usage, arg); 622 } 623 } 624 625 /* 626 * Net entry functions 627 */ 628 static net_entry_t * 629 get_ne_from_table(net_table_t *net_table, char *name) 630 { 631 int count; 632 net_desc_t *nd; 633 net_entry_t *ne = net_table->net_table_head; 634 635 for (count = 0; count < net_table->net_entries; count++) { 636 nd = ne->net_entry_desc; 637 if (strcmp(name, nd->net_desc_name) == 0) 638 return (ne); 639 ne = ne->net_entry_next; 640 } 641 return (NULL); 642 } 643 644 /* Get the entry for the descriptor, if it exists */ 645 static net_desc_t * 646 get_ndesc(net_table_t *net_table, net_desc_t *nd) 647 { 648 int count; 649 net_desc_t *nd1; 650 net_entry_t *ne = net_table->net_table_head; 651 652 for (count = 0; count < net_table->net_entries; count++) { 653 nd1 = ne->net_entry_desc; 654 if (strcmp(nd1->net_desc_name, nd->net_desc_name) == 0 && 655 strcmp(nd1->net_desc_devname, nd->net_desc_devname) == 0 && 656 bcmp(nd1->net_desc_ehost, nd->net_desc_ehost, 657 ETHERADDRL) == 0 && 658 bcmp(nd1->net_desc_edest, nd->net_desc_edest, 659 ETHERADDRL) == 0 && 660 nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid && 661 nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci && 662 nd1->net_desc_sap == nd->net_desc_sap && 663 nd1->net_desc_cpuid == nd->net_desc_cpuid && 664 nd1->net_desc_priority == nd->net_desc_priority && 665 nd1->net_desc_bw_limit == nd->net_desc_bw_limit && 666 nd1->net_desc_sport == nd->net_desc_sport && 667 nd1->net_desc_dport == nd->net_desc_dport && 668 nd1->net_desc_protocol == nd->net_desc_protocol && 669 nd1->net_desc_dsfield == nd->net_desc_dsfield && 670 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr, 671 &nd->net_desc_saddr) && 672 IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr, 673 &nd->net_desc_daddr)) { 674 return (nd1); 675 } 676 ne = ne->net_entry_next; 677 } 678 return (NULL); 679 } 680 681 /* 682 * Update the stat entries. The stats in the file are cumulative, so in order 683 * to have increments, we maintain a reference stat entry, which contains 684 * the stats when the record was first written and a total stat entry, which 685 * maintains the running count. When we want to add a stat entry, if it 686 * the reference stat entry, we don't come here. For subsequent entries, 687 * we get the increment by subtracting the current value from the reference 688 * stat and the total stat. 689 */ 690 static void 691 update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref) 692 { 693 694 /* get the increment */ 695 ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes); 696 ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes); 697 ns1->net_stat_ipackets -= (ref->net_stat_ipackets + 698 ref->net_stat_tipackets); 699 ns1->net_stat_opackets -= (ref->net_stat_opackets + 700 ref->net_stat_topackets); 701 ns1->net_stat_ierrors -= (ref->net_stat_ierrors + 702 ref->net_stat_tierrors); 703 ns1->net_stat_oerrors -= (ref->net_stat_oerrors + 704 ref->net_stat_toerrors); 705 706 /* update total bytes */ 707 ref->net_stat_tibytes += ns1->net_stat_ibytes; 708 ref->net_stat_tobytes += ns1->net_stat_obytes; 709 ref->net_stat_tipackets += ns1->net_stat_ipackets; 710 ref->net_stat_topackets += ns1->net_stat_opackets; 711 ref->net_stat_tierrors += ns1->net_stat_ierrors; 712 ref->net_stat_toerrors += ns1->net_stat_oerrors; 713 714 ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes; 715 ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes; 716 ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets; 717 ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets; 718 ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors; 719 ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors; 720 } 721 722 /* Add the stat entry into the table */ 723 static dladm_status_t 724 add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns) 725 { 726 net_entry_t *ne; 727 728 ne = get_ne_from_table(net_table, ns->net_stat_name); 729 if (ne == NULL) 730 return (DLADM_STATUS_NOMEM); 731 732 /* Ptr to flow desc */ 733 ns->net_stat_desc = ne->net_entry_desc; 734 if (ns->net_stat_desc->net_desc_newrec) { 735 ns->net_stat_desc->net_desc_newrec = B_FALSE; 736 ns->net_stat_isref = B_TRUE; 737 ne->net_entry_sref = ns; 738 } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes || 739 (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) { 740 ns->net_stat_isref = B_TRUE; 741 ne->net_entry_sref = ns; 742 } else { 743 ns->net_stat_isref = B_FALSE; 744 update_stats(ns, ne, ne->net_entry_sref); 745 } 746 if (ne->net_entry_shead == NULL) { 747 ne->net_entry_shead = ns; 748 ne->net_entry_stail = ns; 749 } else { 750 if (!ns->net_stat_isref) { 751 ne->net_entry_ttime += (ns->net_stat_ctime - 752 ne->net_entry_stail->net_stat_ctime); 753 ns->net_stat_tdiff = ns->net_stat_ctime - 754 ne->net_entry_stail->net_stat_ctime; 755 } 756 ne->net_entry_stail->net_stat_next = ns; 757 ne->net_entry_stail = ns; 758 } 759 760 ne->net_entry_scount++; 761 return (DLADM_STATUS_OK); 762 } 763 764 /* Add a flow/link descriptor record to the table */ 765 static dladm_status_t 766 add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs) 767 { 768 net_desc_t *nd; 769 net_desc_t *dnd; 770 int count; 771 ea_object_t scratch; 772 773 if ((nd = calloc(1, sizeof (net_desc_t))) == NULL) 774 return (DLADM_STATUS_NOMEM); 775 nd->net_desc_newrec = B_TRUE; 776 777 for (count = 0; count < nobjs; count++) { 778 if (ea_get_object(ef, &scratch) == -1) { 779 free(nd); 780 return (DLADM_STATUS_NOMEM); 781 } 782 add_desc_item(&scratch, nd); 783 } 784 if ((dnd = get_ndesc(net_table, nd)) != NULL) { 785 dnd->net_desc_newrec = B_TRUE; 786 free(nd); 787 return (DLADM_STATUS_OK); 788 } 789 if (add_desc_to_tbl(net_table, nd) != 0) { 790 free(nd); 791 return (DLADM_STATUS_NOMEM); 792 } 793 return (DLADM_STATUS_OK); 794 } 795 796 /* Make an entry into the time sorted list */ 797 static void 798 addto_time_list(net_table_t *net_table, net_time_entry_t *nt, 799 net_time_entry_t *ntc) 800 { 801 net_stat_t *ns = nt->my_time_stat; 802 net_stat_t *ns1; 803 net_time_entry_t *end; 804 net_time_t *t1; 805 int count; 806 807 t1 = &ns->net_stat_time; 808 809 net_table->net_time_entries++; 810 811 if (net_table->net_time_head == NULL) { 812 net_table->net_time_head = nt; 813 net_table->net_time_tail = nt; 814 } else { 815 net_table->net_time_tail->net_time_entry_next = nt; 816 nt->net_time_entry_prev = net_table->net_time_tail; 817 net_table->net_time_tail = nt; 818 } 819 820 if (net_table->net_ctime_head == NULL) { 821 net_table->net_ctime_head = ntc; 822 net_table->net_ctime_tail = ntc; 823 } else { 824 end = net_table->net_ctime_tail; 825 count = 0; 826 while (count < net_table->net_time_entries - 1) { 827 ns1 = end->my_time_stat; 828 /* Just add it to the tail */ 829 if (compare_date(t1, &ns1->net_stat_time) == 830 NET_DATE_GREATER) { 831 break; 832 } 833 if (strcmp(ns1->net_stat_name, ns->net_stat_name) == 834 0) { 835 ntc->net_time_entry_next = 836 end->net_time_entry_next; 837 if (end->net_time_entry_next != NULL) { 838 end->net_time_entry_next-> 839 net_time_entry_prev = ntc; 840 } else { 841 net_table->net_ctime_tail = ntc; 842 } 843 end->net_time_entry_next = ntc; 844 ntc->net_time_entry_prev = end; 845 return; 846 } 847 count++; 848 end = end->net_time_entry_prev; 849 } 850 net_table->net_ctime_tail->net_time_entry_next = ntc; 851 ntc->net_time_entry_prev = net_table->net_ctime_tail; 852 net_table->net_ctime_tail = ntc; 853 } 854 } 855 856 /* Add stat entry into the lists */ 857 static dladm_status_t 858 add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs) 859 { 860 net_stat_t *ns; 861 int count; 862 ea_object_t scratch; 863 net_time_entry_t *nt; 864 net_time_entry_t *ntc; 865 866 if ((ns = calloc(1, sizeof (net_stat_t))) == NULL) 867 return (DLADM_STATUS_NOMEM); 868 869 if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) { 870 free(ns); 871 return (DLADM_STATUS_NOMEM); 872 } 873 if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) { 874 free(ns); 875 free(nt); 876 return (DLADM_STATUS_NOMEM); 877 } 878 879 nt->my_time_stat = ns; 880 ntc->my_time_stat = ns; 881 882 for (count = 0; count < nobjs; count++) { 883 if (ea_get_object(ef, &scratch) == -1) { 884 free(ns); 885 free(nt); 886 free(ntc); 887 return (DLADM_STATUS_NOMEM); 888 } 889 add_stat_item(&scratch, ns); 890 } 891 if (add_stat_to_tbl(net_table, ns) != 0) { 892 free(ns); 893 free(nt); 894 free(ntc); 895 return (DLADM_STATUS_NOMEM); 896 } 897 addto_time_list(net_table, nt, ntc); 898 return (DLADM_STATUS_OK); 899 } 900 901 /* Free the entire table */ 902 static void 903 free_logtable(net_table_t *net_table) 904 { 905 net_entry_t *head; 906 net_entry_t *next; 907 net_stat_t *ns; 908 net_stat_t *ns1; 909 net_time_entry_t *thead; 910 net_time_entry_t *tnext; 911 912 thead = net_table->net_time_head; 913 while (thead != NULL) { 914 thead->my_time_stat = NULL; 915 tnext = thead->net_time_entry_next; 916 thead->net_time_entry_next = NULL; 917 thead->net_time_entry_prev = NULL; 918 free(thead); 919 thead = tnext; 920 } 921 net_table->net_time_head = NULL; 922 net_table->net_time_tail = NULL; 923 924 thead = net_table->net_ctime_head; 925 while (thead != NULL) { 926 thead->my_time_stat = NULL; 927 tnext = thead->net_time_entry_next; 928 thead->net_time_entry_next = NULL; 929 thead->net_time_entry_prev = NULL; 930 free(thead); 931 thead = tnext; 932 } 933 net_table->net_ctime_head = NULL; 934 net_table->net_ctime_tail = NULL; 935 936 net_table->net_time_entries = 0; 937 938 head = net_table->net_table_head; 939 while (head != NULL) { 940 next = head->net_entry_next; 941 head->net_entry_next = NULL; 942 ns = head->net_entry_shead; 943 while (ns != NULL) { 944 ns1 = ns->net_stat_next; 945 free(ns); 946 ns = ns1; 947 } 948 head->net_entry_scount = 0; 949 head->net_entry_sref = NULL; 950 free(head->net_entry_desc); 951 free(head->net_entry_tstats); 952 free(head); 953 head = next; 954 } 955 net_table->net_table_head = NULL; 956 net_table->net_table_tail = NULL; 957 net_table->net_time_entries = 0; 958 free(net_table); 959 } 960 961 /* Parse the exacct file, and return the parsed table. */ 962 static void * 963 parse_logfile(char *file, int logtype, dladm_status_t *status) 964 { 965 ea_file_t ef; 966 ea_object_t scratch; 967 net_table_t *net_table; 968 969 *status = DLADM_STATUS_OK; 970 if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) { 971 *status = DLADM_STATUS_NOMEM; 972 return (NULL); 973 } 974 if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) { 975 *status = DLADM_STATUS_BADARG; 976 free(net_table); 977 return (NULL); 978 } 979 bzero(&scratch, sizeof (ea_object_t)); 980 while (ea_get_object(&ef, &scratch) != -1) { 981 if (scratch.eo_type != EO_GROUP) { 982 (void) ea_free_item(&scratch, EUP_ALLOC); 983 (void) bzero(&scratch, sizeof (ea_object_t)); 984 continue; 985 } 986 /* Read Link Desc/Stat records */ 987 if (logtype == DLADM_LOGTYPE_FLOW) { 988 /* Flow Descriptor */ 989 if ((scratch.eo_catalog & 990 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) { 991 (void) add_desc(net_table, &ef, 992 scratch.eo_group.eg_nobjs - 1); 993 /* Flow Stats */ 994 } else if ((scratch.eo_catalog & 995 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) { 996 (void) add_stats(net_table, &ef, 997 scratch.eo_group.eg_nobjs - 1); 998 } 999 } else if (logtype == DLADM_LOGTYPE_LINK) { 1000 /* Link Descriptor */ 1001 if ((scratch.eo_catalog & 1002 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) { 1003 (void) add_desc(net_table, &ef, 1004 scratch.eo_group.eg_nobjs - 1); 1005 /* Link Stats */ 1006 } else if ((scratch.eo_catalog & 1007 EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) { 1008 (void) add_stats(net_table, &ef, 1009 scratch.eo_group.eg_nobjs - 1); 1010 } 1011 } else { 1012 if (((scratch.eo_catalog & EXD_DATA_MASK) == 1013 EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog & 1014 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) { 1015 (void) add_desc(net_table, &ef, 1016 scratch.eo_group.eg_nobjs - 1); 1017 } else if (((scratch.eo_catalog & EXD_DATA_MASK) == 1018 EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog & 1019 EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) { 1020 (void) add_stats(net_table, &ef, 1021 scratch.eo_group.eg_nobjs - 1); 1022 } 1023 } 1024 (void) ea_free_item(&scratch, EUP_ALLOC); 1025 (void) bzero(&scratch, sizeof (ea_object_t)); 1026 } 1027 1028 (void) ea_close(&ef); 1029 return ((void *)net_table); 1030 } 1031 1032 /* 1033 * Walk the ctime list. This is used when looking for usage records 1034 * based on a "resource" name. 1035 */ 1036 dladm_status_t 1037 dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype, 1038 char *logfile, char *resource, char *stime, char *etime, void *arg) 1039 { 1040 net_table_t *net_table; 1041 net_time_t st, et; 1042 net_time_entry_t *start; 1043 net_stat_t *ns = NULL; 1044 net_stat_t *nns; 1045 uint64_t tot_time = 0; 1046 uint64_t last_time; 1047 uint64_t tot_bytes = 0; 1048 uint64_t tot_ibytes = 0; 1049 uint64_t tot_obytes = 0; 1050 boolean_t gotstart = B_FALSE; 1051 dladm_status_t status; 1052 dladm_usage_t usage; 1053 int step = 1; 1054 1055 /* Parse the log file */ 1056 net_table = parse_logfile(logfile, logtype, &status); 1057 if (net_table == NULL) 1058 return (status); 1059 1060 if (net_table->net_entries == 0) 1061 return (DLADM_STATUS_OK); 1062 start = net_table->net_ctime_head; 1063 1064 /* Time range */ 1065 status = get_time_range(net_table->net_ctime_head, 1066 net_table->net_ctime_tail, &st, &et, stime, etime); 1067 if (status != DLADM_STATUS_OK) 1068 return (status); 1069 1070 while (start != NULL) { 1071 nns = start->my_time_stat; 1072 1073 /* Get to the resource we are interested in */ 1074 if (strcmp(resource, nns->net_stat_name) != 0) { 1075 start = start->net_time_entry_next; 1076 continue; 1077 } 1078 1079 /* Find the first record */ 1080 if (!gotstart) { 1081 get_starting_point(start, &start, &st, stime, 1082 &last_time); 1083 if (start == NULL) 1084 break; 1085 nns = start->my_time_stat; 1086 gotstart = B_TRUE; 1087 } 1088 1089 /* Write one entry and return if we are out of the range */ 1090 if (etime != NULL && compare_time(&nns->net_stat_time, &et) 1091 == NET_TIME_GREATER) { 1092 if (tot_bytes != 0) { 1093 bcopy(ns->net_stat_name, &usage.du_name, 1094 sizeof (usage.du_name)); 1095 bcopy(&last_time, &usage.du_stime, 1096 sizeof (usage.du_stime)); 1097 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1098 sizeof (usage.du_etime)); 1099 usage.du_rbytes = tot_ibytes; 1100 usage.du_obytes = tot_obytes; 1101 usage.du_bandwidth = tot_bytes*8/tot_time; 1102 usage.du_last = B_TRUE; 1103 fn(&usage, arg); 1104 } 1105 return (DLADM_STATUS_OK); 1106 } 1107 1108 /* 1109 * If this is a reference entry, just print what we have 1110 * and proceed. 1111 */ 1112 if (nns->net_stat_isref) { 1113 if (tot_bytes != 0) { 1114 bcopy(&nns->net_stat_name, &usage.du_name, 1115 sizeof (usage.du_name)); 1116 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1117 sizeof (usage.du_stime)); 1118 usage.du_rbytes = tot_ibytes; 1119 usage.du_obytes = tot_obytes; 1120 usage.du_bandwidth = tot_bytes*8/tot_time; 1121 usage.du_last = B_TRUE; 1122 fn(&usage, arg); 1123 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1124 tot_obytes, step); 1125 } 1126 last_time = nns->net_stat_ctime; 1127 start = start->net_time_entry_next; 1128 continue; 1129 } 1130 1131 ns = nns; 1132 if (--step == 0) { 1133 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1134 tot_ibytes += ns->net_stat_ibytes; 1135 tot_obytes += ns->net_stat_obytes; 1136 tot_time += ns->net_stat_tdiff; 1137 bcopy(&ns->net_stat_name, &usage.du_name, 1138 sizeof (usage.du_name)); 1139 bcopy(&last_time, &usage.du_stime, 1140 sizeof (usage.du_stime)); 1141 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1142 sizeof (usage.du_etime)); 1143 usage.du_rbytes = tot_ibytes; 1144 usage.du_obytes = tot_obytes; 1145 usage.du_bandwidth = tot_bytes*8/tot_time; 1146 usage.du_last = B_TRUE; 1147 fn(&usage, arg); 1148 1149 NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes, 1150 tot_obytes, step); 1151 last_time = ns->net_stat_ctime; 1152 } else { 1153 tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes; 1154 tot_ibytes += ns->net_stat_ibytes; 1155 tot_obytes += ns->net_stat_obytes; 1156 tot_time += ns->net_stat_tdiff; 1157 } 1158 start = start->net_time_entry_next; 1159 } 1160 1161 if (tot_bytes != 0) { 1162 bcopy(&ns->net_stat_name, &usage.du_name, 1163 sizeof (usage.du_name)); 1164 bcopy(&last_time, &usage.du_stime, 1165 sizeof (usage.du_stime)); 1166 bcopy(&ns->net_stat_ctime, &usage.du_etime, 1167 sizeof (usage.du_etime)); 1168 usage.du_rbytes = tot_ibytes; 1169 usage.du_obytes = tot_obytes; 1170 usage.du_bandwidth = tot_bytes*8/tot_time; 1171 usage.du_last = B_TRUE; 1172 fn(&usage, arg); 1173 } 1174 1175 free_logtable(net_table); 1176 return (status); 1177 } 1178 1179 /* 1180 * Walk the time sorted list if a resource is not specified. 1181 */ 1182 dladm_status_t 1183 dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype, 1184 char *logfile, char *stime, char *etime, void *arg) 1185 { 1186 net_table_t *net_table; 1187 net_time_entry_t *start; 1188 net_stat_t *ns = NULL, *nns; 1189 net_time_t st, et, *t1; 1190 net_desc_t *nd; 1191 net_entry_t *ne; 1192 net_plot_entry_t *pe; 1193 int count; 1194 int step = 1; 1195 int nentries = 0, pentries = 0; 1196 uint64_t last_time; 1197 dladm_status_t status; 1198 1199 /* Parse the log file */ 1200 net_table = parse_logfile(logfile, logtype, &status); 1201 if (net_table == NULL) 1202 return (status); 1203 1204 if (net_table->net_entries == 0) 1205 return (DLADM_STATUS_OK); 1206 start = net_table->net_time_head; 1207 1208 /* Find the first and last records and starting point */ 1209 status = get_time_range(net_table->net_time_head, 1210 net_table->net_time_tail, &st, &et, stime, etime); 1211 if (status != DLADM_STATUS_OK) 1212 return (status); 1213 get_starting_point(start, &start, &st, stime, &last_time); 1214 /* 1215 * Could assert to be non-null, since get_time_range() 1216 * would have adjusted. 1217 */ 1218 if (start == NULL) 1219 return (DLADM_STATUS_BADTIMEVAL); 1220 1221 /* 1222 * Collect entries for all resources in a time slot before 1223 * writing to the file. 1224 */ 1225 nentries = net_table->net_entries; 1226 1227 pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1); 1228 if (pe == NULL) 1229 return (DLADM_STATUS_NOMEM); 1230 1231 ne = net_table->net_table_head; 1232 for (count = 0; count < nentries; count++) { 1233 nd = ne->net_entry_desc; 1234 pe[count].net_pe_name = nd->net_desc_name; 1235 ne = ne->net_entry_next; 1236 } 1237 1238 clear_pe(pe, nentries, &pentries); 1239 1240 /* Write header to file */ 1241 /* add_pe_to_file(fn, pe, ns, nentries, arg); */ 1242 1243 t1 = &start->my_time_stat->net_stat_time; 1244 1245 while (start != NULL) { 1246 1247 nns = start->my_time_stat; 1248 /* 1249 * We have crossed the time boundary, check if we need to 1250 * print out now. 1251 */ 1252 if (compare_time(&nns->net_stat_time, t1) == 1253 NET_TIME_GREATER) { 1254 /* return if we are out of the range */ 1255 if (etime != NULL && 1256 compare_time(&nns->net_stat_time, &et) == 1257 NET_TIME_GREATER) { 1258 if (pentries > 0) { 1259 add_pe_to_file(fn, pe, ns, nentries, 1260 arg); 1261 clear_pe(pe, nentries, &pentries); 1262 } 1263 free(pe); 1264 return (DLADM_STATUS_OK); 1265 } 1266 /* update the stats from the ns. */ 1267 t1 = &nns->net_stat_time; 1268 last_time = ns->net_stat_ctime; 1269 if (--step == 0) { 1270 if (pentries > 0) { 1271 add_pe_to_file(fn, pe, ns, nentries, 1272 arg); 1273 clear_pe(pe, nentries, &pentries); 1274 } 1275 step = 1; 1276 } 1277 } 1278 1279 /* 1280 * if this is a reference entry, just print what we have 1281 * for this resource and proceed. We will end up writing 1282 * the stats for all the entries when we hit a ref element, 1283 * which means 'steps' for some might not be accurate, but 1284 * that is fine, the alternative is to write only the 1285 * resource for which we hit a reference entry. 1286 */ 1287 if (nns->net_stat_isref) { 1288 if (pentries > 0) { 1289 add_pe_to_file(fn, pe, ns, nentries, arg); 1290 clear_pe(pe, nentries, &pentries); 1291 } 1292 step = 1; 1293 } else { 1294 update_pe(pe, nns, nentries, &pentries, last_time); 1295 } 1296 ns = nns; 1297 start = start->net_time_entry_next; 1298 } 1299 1300 if (pentries > 0) 1301 add_pe_to_file(fn, pe, ns, nentries, arg); 1302 1303 free(pe); 1304 free_logtable(net_table); 1305 1306 return (DLADM_STATUS_OK); 1307 } 1308 1309 dladm_status_t 1310 dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype, 1311 char *logfile, void *arg) 1312 { 1313 net_table_t *net_table; 1314 net_entry_t *ne; 1315 net_desc_t *nd; 1316 net_stat_t *ns; 1317 int count; 1318 dladm_usage_t usage; 1319 dladm_status_t status; 1320 1321 /* Parse the log file */ 1322 net_table = parse_logfile(logfile, logtype, &status); 1323 if (net_table == NULL) 1324 return (status); 1325 1326 if (net_table->net_entries == 0) 1327 return (DLADM_STATUS_OK); 1328 1329 ne = net_table->net_table_head; 1330 for (count = 0; count < net_table->net_entries; count++) { 1331 ns = ne->net_entry_tstats; 1332 nd = ne->net_entry_desc; 1333 1334 if (ns->net_stat_ibytes + ns->net_stat_obytes == 0) 1335 continue; 1336 bcopy(&nd->net_desc_name, &usage.du_name, 1337 sizeof (usage.du_name)); 1338 usage.du_duration = ne->net_entry_ttime; 1339 usage.du_ipackets = ns->net_stat_ipackets; 1340 usage.du_rbytes = ns->net_stat_ibytes; 1341 usage.du_opackets = ns->net_stat_opackets; 1342 usage.du_obytes = ns->net_stat_obytes; 1343 usage.du_bandwidth = 1344 (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 / 1345 usage.du_duration; 1346 usage.du_last = (count == net_table->net_entries-1); 1347 fn(&usage, arg); 1348 1349 ne = ne->net_entry_next; 1350 } 1351 1352 free_logtable(net_table); 1353 return (DLADM_STATUS_OK); 1354 } 1355 1356 /* 1357 * Walk the ctime list and display the dates of the records. 1358 */ 1359 dladm_status_t 1360 dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype, 1361 char *logfile, char *resource, void *arg) 1362 { 1363 net_table_t *net_table; 1364 net_time_entry_t *start; 1365 net_stat_t *nns; 1366 net_time_t st; 1367 net_time_t *lasttime = NULL; 1368 uint64_t last_time; 1369 boolean_t gotstart = B_FALSE; 1370 dladm_status_t status; 1371 dladm_usage_t usage; 1372 1373 /* Parse the log file */ 1374 net_table = parse_logfile(logfile, logtype, &status); 1375 if (net_table == NULL) 1376 return (status); 1377 1378 if (net_table->net_entries == 0) 1379 return (DLADM_STATUS_OK); 1380 1381 start = net_table->net_ctime_head; 1382 1383 while (start != NULL) { 1384 nns = start->my_time_stat; 1385 1386 /* get to the resource we are interested in */ 1387 if (resource != NULL) { 1388 if (strcmp(resource, nns->net_stat_name) != 0) { 1389 start = start->net_time_entry_next; 1390 continue; 1391 } 1392 } 1393 1394 /* get the starting point in the logfile */ 1395 if (!gotstart) { 1396 get_starting_point(start, &start, &st, NULL, 1397 &last_time); 1398 if (start == NULL) 1399 break; 1400 nns = start->my_time_stat; 1401 gotstart = B_TRUE; 1402 } 1403 1404 if (lasttime == NULL || 1405 compare_date(&nns->net_stat_time, lasttime) == 1406 NET_DATE_GREATER) { 1407 bzero(&usage, sizeof (dladm_usage_t)); 1408 (void) strlcpy(usage.du_name, nns->net_stat_name, 1409 sizeof (usage.du_name)); 1410 bcopy(&nns->net_stat_ctime, &usage.du_stime, 1411 sizeof (usage.du_stime)); 1412 fn(&usage, arg); 1413 lasttime = &nns->net_stat_time; 1414 } 1415 1416 start = start->net_time_entry_next; 1417 continue; 1418 } 1419 1420 free_logtable(net_table); 1421 return (status); 1422 } 1423