tree.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /* $Id: tree.c 1900 2007-08-26 19:47:53Z aturner $ */
  2. /*
  3. * Copyright (c) 2001-2007 Aaron Turner.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the names of the copyright owners nor the names of its
  16. * contributors may be used to endorse or promote products derived from
  17. * this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  20. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  23. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  25. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  27. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  28. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  29. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. #include "config.h"
  32. #include "defines.h"
  33. #include "common.h"
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include "tree.h"
  38. #include "tcpprep.h"
  39. #include "tcpprep_opts.h"
  40. extern tcpr_data_tree_t treeroot;
  41. extern tcpprep_opt_t options;
  42. #ifdef DEBUG
  43. extern int debug;
  44. #endif
  45. /* static buffer used by tree_print*() functions */
  46. char tree_print_buff[TREEPRINTBUFFLEN];
  47. static tcpr_tree_t *new_tree();
  48. static tcpr_tree_t *packet2tree(const u_char *);
  49. static char *tree_print(tcpr_data_tree_t *);
  50. static char *tree_printnode(const char *, const tcpr_tree_t *);
  51. static void tree_buildcidr(tcpr_data_tree_t *, tcpr_buildcidr_t *);
  52. static int tree_checkincidr(tcpr_data_tree_t *, tcpr_buildcidr_t *);
  53. RB_PROTOTYPE(tcpr_data_tree_s, tcpr_tree_s, node, tree_comp)
  54. RB_GENERATE(tcpr_data_tree_s, tcpr_tree_s, node, tree_comp)
  55. /**
  56. * used with rbwalk to walk a tree and generate cidr_t * cidrdata.
  57. * is smart enough to prevent dupes. void * arg is cast to bulidcidr_t
  58. */
  59. void
  60. tree_buildcidr(tcpr_data_tree_t *treeroot, tcpr_buildcidr_t * bcdata)
  61. {
  62. tcpr_tree_t *node = NULL;
  63. tcpr_cidr_t *newcidr = NULL;
  64. unsigned long network = 0;
  65. unsigned long mask = ~0; /* turn on all bits */
  66. dbg(1, "Running: tree_buildcidr()");
  67. RB_FOREACH(node, tcpr_data_tree_s, treeroot) {
  68. /* we only check types that are vaild */
  69. if (bcdata->type != DIR_ANY) /* don't check if we're adding ANY */
  70. if (bcdata->type != node->type) /* no match, exit early */
  71. return;
  72. /*
  73. * in cases of leaves and last visit add to cidrdata if
  74. * necessary
  75. */
  76. dbgx(4, "Checking if %s exists in cidrdata...", get_addr2name4(node->ip, RESOLVE));
  77. if (!check_ip_cidr(options.cidrdata, node->ip)) { /* if we exist, abort */
  78. dbgx(3, "Node %s doesn't exist... creating.",
  79. get_addr2name4(node->ip, RESOLVE));
  80. newcidr = new_cidr();
  81. newcidr->masklen = bcdata->masklen;
  82. network = node->ip & (mask << (32 - bcdata->masklen));
  83. dbgx(3, "Using network: %s",
  84. get_addr2name4(network, RESOLVE));
  85. newcidr->network = network;
  86. add_cidr(&options.cidrdata, &newcidr);
  87. }
  88. }
  89. }
  90. /**
  91. * uses rbwalk to check to see if a given ip address of a given type in the
  92. * tree is inside any of the cidrdata
  93. */
  94. static int
  95. tree_checkincidr(tcpr_data_tree_t *treeroot, tcpr_buildcidr_t * bcdata)
  96. {
  97. tcpr_tree_t *node = NULL;
  98. RB_FOREACH(node, tcpr_data_tree_s, treeroot) {
  99. /* we only check types that are vaild */
  100. if (bcdata->type != DIR_ANY) /* don't check if we're adding ANY */
  101. if (bcdata->type != node->type) /* no match, exit early */
  102. return 0;
  103. /*
  104. * in cases of leaves and last visit add to cidrdata if
  105. * necessary
  106. */
  107. if (check_ip_cidr(options.cidrdata, node->ip)) /* if we exist, abort */
  108. return 1;
  109. }
  110. return 0;
  111. }
  112. /**
  113. * processes the tree using rbwalk / tree2cidr to generate a CIDR
  114. * used for 2nd pass, router mode
  115. *
  116. * returns > 0 for success (the mask len), 0 for fail
  117. */
  118. int
  119. process_tree(void)
  120. {
  121. int mymask = 0;
  122. tcpr_buildcidr_t *bcdata;
  123. dbg(1, "Running: process_tree()");
  124. bcdata = (tcpr_buildcidr_t *)safe_malloc(sizeof(tcpr_buildcidr_t));
  125. for (mymask = options.max_mask; mymask <= options.min_mask; mymask++) {
  126. dbgx(1, "Current mask: %u", mymask);
  127. /* set starting vals */
  128. bcdata->type = DIR_SERVER;
  129. bcdata->masklen = mymask;
  130. /* build cidrdata with servers */
  131. tree_buildcidr(&treeroot, bcdata);
  132. /* calculate types of all IP's */
  133. tree_calculate(&treeroot);
  134. /* try to find clients in cidrdata */
  135. bcdata->type = DIR_CLIENT;
  136. if (! tree_checkincidr(&treeroot, bcdata)) { /* didn't find any clients in cidrdata */
  137. safe_free(bcdata);
  138. return (mymask); /* success! */
  139. }
  140. else {
  141. destroy_cidr(options.cidrdata); /* clean up after our mess */
  142. options.cidrdata = NULL;
  143. }
  144. }
  145. safe_free(bcdata);
  146. /* we failed to find a vaild cidr list */
  147. notice("Unable to determine any IP addresses as a clients.");
  148. notice("Perhaps you should change the --ratio, --minmask/maxmask settings, or try another mode?");
  149. return (0);
  150. }
  151. /*
  152. * processes rbdata to bulid cidrdata based upon the
  153. * given type (SERVER, CLIENT, UNKNOWN) using the given masklen
  154. *
  155. * is smart enough to prevent dupes
  156. void
  157. tcpr_tree_to_cidr(const int masklen, const int type)
  158. {
  159. }
  160. */
  161. /**
  162. * Checks to see if an IP is client or server by finding it in the tree
  163. * returns TCPR_DIR_C2S or TCPR_DIR_S2C or -1 on error
  164. * if mode = UNKNOWN, then abort on unknowns
  165. * if mode = CLIENT, then unknowns become clients
  166. * if mode = SERVER, then unknowns become servers
  167. */
  168. tcpr_dir_t
  169. check_ip_tree(const int mode, const unsigned long ip)
  170. {
  171. tcpr_tree_t *node = NULL, *finder = NULL;
  172. finder = new_tree();
  173. finder->ip = ip;
  174. node = RB_FIND(tcpr_data_tree_s, &treeroot, finder);
  175. if (node == NULL && mode == DIR_UNKNOWN)
  176. errx(1, "%s (%lu) is an unknown system... aborting.!\n"
  177. "Try a different auto mode (-n router|client|server)",
  178. get_addr2name4(ip, RESOLVE), ip);
  179. #ifdef DEBUG
  180. switch (node->type) {
  181. case DIR_SERVER:
  182. dbgx(1, "DIR_SERVER: %s", get_addr2name4(ip, RESOLVE));
  183. break;
  184. case DIR_CLIENT:
  185. dbgx(1, "DIR_CLIENT: %s", get_addr2name4(ip, RESOLVE));
  186. break;
  187. case DIR_UNKNOWN:
  188. dbgx(1, "DIR_UNKNOWN: %s", get_addr2name4(ip, RESOLVE));
  189. break;
  190. case DIR_ANY:
  191. dbgx(1, "DIR_ANY: %s", get_addr2name4(ip, RESOLVE));
  192. break;
  193. }
  194. #endif
  195. /*
  196. * FIXME: Is this logic correct? I think this might be backwards :(
  197. */
  198. /* return node type if we found the node, else return the default (mode) */
  199. if (node != NULL) {
  200. switch (node->type) {
  201. case DIR_SERVER:
  202. return TCPR_DIR_C2S;
  203. break;
  204. case DIR_CLIENT:
  205. return TCPR_DIR_S2C;
  206. break;
  207. case DIR_UNKNOWN:
  208. case DIR_ANY:
  209. /* use our current mode to determine return code */
  210. goto return_unknown;
  211. default:
  212. errx(1, "Node for %s has invalid type: %d", get_addr2name4(ip, RESOLVE), node->type);
  213. }
  214. }
  215. return_unknown:
  216. switch (mode) {
  217. case DIR_SERVER:
  218. return TCPR_DIR_C2S;
  219. break;
  220. case DIR_CLIENT:
  221. return TCPR_DIR_S2C;
  222. break;
  223. default:
  224. return -1;
  225. }
  226. }
  227. /**
  228. * Parses the IP header of the given packet (data) to get the SRC/DST IP
  229. * addresses. If the SRC IP doesn't exist in the TREE, we add it as a
  230. * client, if the DST IP doesn't exist in the TREE, we add it as a server
  231. */
  232. void
  233. add_tree_first(const u_char *data)
  234. {
  235. tcpr_tree_t *newnode = NULL, *findnode;
  236. eth_hdr_t *eth_hdr = NULL;
  237. ipv4_hdr_t ip_hdr;
  238. assert(data);
  239. /*
  240. * first add/find the source IP/client
  241. */
  242. newnode = new_tree();
  243. eth_hdr = (eth_hdr_t *) (data);
  244. /* prevent issues with byte alignment, must memcpy */
  245. memcpy(&ip_hdr, (data + TCPR_ETH_H), TCPR_IPV4_H);
  246. /* copy over the source ip, and values to gurantee this a client */
  247. newnode->ip = ip_hdr.ip_src.s_addr;
  248. newnode->type = DIR_CLIENT;
  249. newnode->client_cnt = 1000;
  250. findnode = RB_FIND(tcpr_data_tree_s, &treeroot, newnode);
  251. /* if we didn't find it, add it to the tree, else free it */
  252. if (findnode == NULL) {
  253. RB_INSERT(tcpr_data_tree_s, &treeroot, newnode);
  254. } else {
  255. safe_free(newnode);
  256. }
  257. /*
  258. * now add/find the destination IP/server
  259. */
  260. newnode = new_tree();
  261. eth_hdr = (eth_hdr_t *) (data);
  262. memcpy(&ip_hdr, (data + TCPR_ETH_H), TCPR_IPV4_H);
  263. newnode->ip = ip_hdr.ip_dst.s_addr;
  264. newnode->type = DIR_SERVER;
  265. newnode->server_cnt = 1000;
  266. findnode = RB_FIND(tcpr_data_tree_s, &treeroot, newnode);
  267. if (findnode == NULL) {
  268. RB_INSERT(tcpr_data_tree_s, &treeroot, newnode);
  269. } else {
  270. safe_free(newnode);
  271. }
  272. }
  273. /**
  274. * adds an entry to the tree (phase 1 of auto mode). We add each host
  275. * to the tree if it doesn't yet exist. We go through and track:
  276. * - number of times each host acts as a client or server
  277. * - the way the host acted the first time we saw it (client or server)
  278. */
  279. void
  280. add_tree(const unsigned long ip, const u_char * data)
  281. {
  282. tcpr_tree_t *node = NULL, *newnode = NULL;
  283. assert(data);
  284. newnode = packet2tree(data);
  285. assert(ip == newnode->ip);
  286. if (newnode->type == DIR_UNKNOWN) {
  287. /* couldn't figure out if packet was client or server */
  288. dbgx(2, "%s (%lu) unknown client/server",
  289. get_addr2name4(newnode->ip, RESOLVE), newnode->ip);
  290. }
  291. /* try to find a simular entry in the tree */
  292. node = RB_FIND(tcpr_data_tree_s, &treeroot, newnode);
  293. dbgx(3, "%s", tree_printnode("add_tree", node));
  294. /* new entry required */
  295. if (node == NULL) {
  296. /* increment counters */
  297. if (newnode->type == DIR_SERVER) {
  298. newnode->server_cnt++;
  299. }
  300. else if (newnode->type == DIR_CLIENT) {
  301. newnode->client_cnt++;
  302. }
  303. /* insert it in */
  304. RB_INSERT(tcpr_data_tree_s, &treeroot, newnode);
  305. }
  306. else {
  307. /* we found something, so update it */
  308. dbgx(2, " node: %p\nnewnode: %p", node, newnode);
  309. dbgx(3, "%s", tree_printnode("update node", node));
  310. /* increment counter */
  311. if (newnode->type == DIR_SERVER) {
  312. node->server_cnt++;
  313. }
  314. else if (newnode->type == DIR_CLIENT) {
  315. /* temp debug code */
  316. node->client_cnt++;
  317. }
  318. /* didn't insert it, so free it */
  319. safe_free(newnode);
  320. }
  321. dbg(2, "------- START NEXT -------");
  322. dbgx(3, "%s", tree_print(&treeroot));
  323. }
  324. /**
  325. * calculates wether each node in the tree is a client, server, or unknown for each node in the tree
  326. */
  327. void
  328. tree_calculate(tcpr_data_tree_t *treeroot)
  329. {
  330. tcpr_tree_t *node;
  331. dbg(1, "Running tree_calculate()");
  332. RB_FOREACH(node, tcpr_data_tree_s, treeroot) {
  333. dbgx(4, "Processing %s", get_addr2name4(node->ip, RESOLVE));
  334. if ((node->server_cnt > 0) || (node->client_cnt > 0)) {
  335. /* type based on: server >= (client*ratio) */
  336. if ((double)node->server_cnt >= (double)node->client_cnt * options.ratio) {
  337. node->type = DIR_SERVER;
  338. dbgx(3, "Setting %s to server",
  339. get_addr2name4(node->ip, RESOLVE));
  340. }
  341. else {
  342. node->type = DIR_CLIENT;
  343. dbgx(3, "Setting %s to client",
  344. get_addr2name4(node->ip, RESOLVE));
  345. }
  346. }
  347. else { /* IP had no client or server connections */
  348. node->type = DIR_UNKNOWN;
  349. dbgx(3, "Setting %s to unknown",
  350. get_addr2name4(node->ip, RESOLVE));
  351. }
  352. }
  353. }
  354. /**
  355. * tree_comp(), called by rbsearch compares two treees and returns:
  356. * 1 = first > second
  357. * -1 = first < second
  358. * 0 = first = second
  359. * based upon the ip address stored
  360. *
  361. */
  362. int
  363. tree_comp(tcpr_tree_t *t1, tcpr_tree_t *t2)
  364. {
  365. if (t1->ip > t2->ip) {
  366. dbgx(2, "%s > %s", get_addr2name4(t1->ip, RESOLVE),
  367. get_addr2name4(t2->ip, RESOLVE));
  368. return 1;
  369. }
  370. if (t1->ip < t2->ip) {
  371. dbgx(2, "%s < %s", get_addr2name4(t1->ip, RESOLVE),
  372. get_addr2name4(t2->ip, RESOLVE));
  373. return -1;
  374. }
  375. dbgx(2, "%s = %s", get_addr2name4(t1->ip, RESOLVE),
  376. get_addr2name4(t2->ip, RESOLVE));
  377. return 0;
  378. }
  379. /**
  380. * creates a new TREE * with reasonable defaults
  381. */
  382. static tcpr_tree_t *
  383. new_tree()
  384. {
  385. tcpr_tree_t *node;
  386. node = (tcpr_tree_t *)safe_malloc(sizeof(tcpr_tree_t));
  387. memset(node, '\0', sizeof(tcpr_tree_t));
  388. node->server_cnt = 0;
  389. node->client_cnt = 0;
  390. node->type = DIR_UNKNOWN;
  391. node->masklen = -1;
  392. node->ip = 0;
  393. return (node);
  394. }
  395. /**
  396. * returns a struct of TREE * from a packet header
  397. * and sets the type to be SERVER or CLIENT or UNKNOWN
  398. * if it's an undefined packet, we return -1 for the type
  399. * the u_char * data should be the data that is passed by pcap_dispatch()
  400. */
  401. tcpr_tree_t *
  402. packet2tree(const u_char * data)
  403. {
  404. tcpr_tree_t *node = NULL;
  405. eth_hdr_t *eth_hdr = NULL;
  406. ipv4_hdr_t ip_hdr;
  407. tcp_hdr_t tcp_hdr;
  408. udp_hdr_t udp_hdr;
  409. icmpv4_hdr_t icmp_hdr;
  410. dnsv4_hdr_t dnsv4_hdr;
  411. node = new_tree();
  412. eth_hdr = (eth_hdr_t *) (data);
  413. /* prevent issues with byte alignment, must memcpy */
  414. memcpy(&ip_hdr, (data + TCPR_ETH_H), TCPR_IPV4_H);
  415. /* copy over the source mac */
  416. strncpy((char *)node->mac, (char *)eth_hdr->ether_shost, 6);
  417. /* copy over the source ip */
  418. node->ip = ip_hdr.ip_src.s_addr;
  419. /*
  420. * TCP
  421. */
  422. if (ip_hdr.ip_p == IPPROTO_TCP) {
  423. dbgx(3, "%s uses TCP... ",
  424. get_addr2name4(ip_hdr.ip_src.s_addr, RESOLVE));
  425. /* memcpy it over to prevent alignment issues */
  426. memcpy(&tcp_hdr, (data + TCPR_ETH_H + (ip_hdr.ip_hl * 4)),
  427. TCPR_TCP_H);
  428. /* ftp-data is going to skew our results so we ignore it */
  429. if (tcp_hdr.th_sport == 20) {
  430. return (node);
  431. }
  432. /* set TREE->type based on TCP flags */
  433. if (tcp_hdr.th_flags == TH_SYN) {
  434. node->type = DIR_CLIENT;
  435. dbg(3, "is a client");
  436. }
  437. else if (tcp_hdr.th_flags == (TH_SYN | TH_ACK)) {
  438. node->type = DIR_SERVER;
  439. dbg(3, "is a server");
  440. }
  441. else {
  442. dbg(3, "is an unknown");
  443. }
  444. /*
  445. * UDP
  446. */
  447. }
  448. else if (ip_hdr.ip_p == IPPROTO_UDP) {
  449. /* memcpy over to prevent alignment issues */
  450. memcpy(&udp_hdr, (data + TCPR_ETH_H + (ip_hdr.ip_hl * 4)),
  451. TCPR_UDP_H);
  452. dbgx(3, "%s uses UDP... ",
  453. get_addr2name4(ip_hdr.ip_src.s_addr, RESOLVE));
  454. switch (ntohs(udp_hdr.uh_dport)) {
  455. case 0x0035: /* dns */
  456. /* prevent memory alignment issues */
  457. memcpy(&dnsv4_hdr,
  458. (data + TCPR_ETH_H + (ip_hdr.ip_hl * 4) + TCPR_UDP_H),
  459. TCPR_DNS_H);
  460. if (dnsv4_hdr.flags & DNS_QUERY_FLAG) {
  461. /* bit set, response */
  462. node->type = DIR_SERVER;
  463. dbg(3, "is a dns server");
  464. }
  465. else {
  466. /* bit not set, query */
  467. node->type = DIR_CLIENT;
  468. dbg(3, "is a dns client");
  469. }
  470. return (node);
  471. break;
  472. default:
  473. break;
  474. }
  475. switch (ntohs(udp_hdr.uh_sport)) {
  476. case 0x0035: /* dns */
  477. /* prevent memory alignment issues */
  478. memcpy(&dnsv4_hdr,
  479. (data + TCPR_ETH_H + (ip_hdr.ip_hl * 4) + TCPR_UDP_H),
  480. TCPR_DNS_H);
  481. if ((dnsv4_hdr.flags & 0x7FFFF) ^ DNS_QUERY_FLAG) {
  482. /* bit set, response */
  483. node->type = DIR_SERVER;
  484. dbg(3, "is a dns server");
  485. }
  486. else {
  487. /* bit not set, query */
  488. node->type = DIR_CLIENT;
  489. dbg(3, "is a dns client");
  490. }
  491. return (node);
  492. break;
  493. default:
  494. dbgx(3, "unknown UDP protocol: %hu->%hu", udp_hdr.uh_sport,
  495. udp_hdr.uh_dport);
  496. break;
  497. }
  498. /*
  499. * ICMP
  500. */
  501. }
  502. else if (ip_hdr.ip_p == IPPROTO_ICMP) {
  503. /* prevent alignment issues */
  504. memcpy(&icmp_hdr, (data + TCPR_ETH_H + (ip_hdr.ip_hl * 4)),
  505. TCPR_ICMPV4_H);
  506. dbgx(3, "%s uses ICMP... ",
  507. get_addr2name4(ip_hdr.ip_src.s_addr, RESOLVE));
  508. /*
  509. * if port unreachable, then source == server, dst == client
  510. */
  511. if ((icmp_hdr.icmp_type == ICMP_UNREACH) &&
  512. (icmp_hdr.icmp_code == ICMP_UNREACH_PORT)) {
  513. node->type = DIR_SERVER;
  514. dbg(3, "is a server with a closed port");
  515. }
  516. }
  517. return (node);
  518. }
  519. /**
  520. * prints out a node of the tree to stderr
  521. */
  522. static char *
  523. tree_printnode(const char *name, const tcpr_tree_t *node)
  524. {
  525. memset(&tree_print_buff, '\0', TREEPRINTBUFFLEN);
  526. if (node == NULL) {
  527. snprintf(tree_print_buff, TREEPRINTBUFFLEN, "%s node is null", name);
  528. }
  529. else {
  530. snprintf(tree_print_buff, TREEPRINTBUFFLEN,
  531. "-- %s: %p\nIP: %s\nMask: %d\nSrvr: %d\nClnt: %d\n",
  532. name, (void *)node, get_addr2name4(node->ip, RESOLVE),
  533. node->masklen, node->server_cnt, node->client_cnt);
  534. if (node->type == DIR_SERVER) {
  535. strlcat(tree_print_buff, "Type: Server\n--\n", TREEPRINTBUFFLEN);
  536. }
  537. else {
  538. strlcat(tree_print_buff, "Type: Client\n--", TREEPRINTBUFFLEN);
  539. }
  540. }
  541. return (tree_print_buff);
  542. }
  543. /**
  544. * prints out the entire tree
  545. */
  546. static char *
  547. tree_print(tcpr_data_tree_t *treeroot)
  548. {
  549. tcpr_tree_t *node = NULL;
  550. memset(&tree_print_buff, '\0', TREEPRINTBUFFLEN);
  551. RB_FOREACH(node, tcpr_data_tree_s, treeroot) {
  552. tree_printnode("my node", node);
  553. }
  554. return (tree_print_buff);
  555. }
  556. /*
  557. Local Variables:
  558. mode:c
  559. indent-tabs-mode:nil
  560. c-basic-offset:4
  561. End:
  562. */