peer_select.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:17k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: peer_select.c,v 1.98 1999/01/29 23:01:07 wessels Exp $
  3.  *
  4.  * DEBUG: section 44    Peer Selection Algorithm
  5.  * AUTHOR: Duane Wessels
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. const char *hier_strings[] =
  36. {
  37.     "NONE",
  38.     "DIRECT",
  39.     "SIBLING_HIT",
  40.     "PARENT_HIT",
  41.     "DEFAULT_PARENT",
  42.     "SINGLE_PARENT",
  43.     "FIRST_UP_PARENT",
  44.     "FIRST_PARENT_MISS",
  45.     "CLOSEST_PARENT_MISS",
  46.     "CLOSEST_PARENT",
  47.     "CLOSEST_DIRECT",
  48.     "NO_DIRECT_FAIL",
  49.     "SOURCE_FASTEST",
  50.     "ROUNDROBIN_PARENT",
  51. #if USE_CACHE_DIGESTS
  52.     "CACHE_DIGEST_HIT",
  53. #endif
  54. #if USE_CARP
  55.     "CARP",
  56. #endif
  57.     "ANY_PARENT",
  58.     "INVALID CODE"
  59. };
  60. static struct {
  61.     int timeouts;
  62. } PeerStats;
  63. static char *DirectStr[] =
  64. {
  65.     "DIRECT_UNKNOWN",
  66.     "DIRECT_NO",
  67.     "DIRECT_MAYBE",
  68.     "DIRECT_YES"
  69. };
  70. static void peerSelectFoo(ps_state *);
  71. static void peerPingTimeout(void *data);
  72. static void peerSelectCallback(ps_state * psstate);
  73. static IRCB peerHandlePingReply;
  74. static void peerSelectStateFree(ps_state * psstate);
  75. static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
  76. #if USE_HTCP
  77. static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
  78. static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
  79. #endif
  80. static int peerCheckNetdbDirect(ps_state * psstate);
  81. static void peerGetSomeNeighbor(ps_state *);
  82. static void peerGetSomeNeighborReplies(ps_state *);
  83. static void peerGetSomeDirect(ps_state *);
  84. static void peerGetSomeParent(ps_state *);
  85. static void peerAddFwdServer(FwdServer **, peer *, hier_code);
  86. static void
  87. peerSelectStateFree(ps_state * psstate)
  88. {
  89.     if (psstate->acl_checklist) {
  90. debug(44, 1) ("calling aclChecklistFree() from peerSelectStateFreen");
  91. aclChecklistFree(psstate->acl_checklist);
  92.     }
  93.     requestUnlink(psstate->request);
  94.     psstate->request = NULL;
  95.     if (psstate->entry) {
  96. assert(psstate->entry->ping_status != PING_WAITING);
  97. storeUnlockObject(psstate->entry);
  98. psstate->entry = NULL;
  99.     }
  100.     cbdataFree(psstate);
  101. }
  102. int
  103. peerSelectIcpPing(request_t * request, int direct, StoreEntry * entry)
  104. {
  105.     int n;
  106.     assert(entry);
  107.     assert(entry->ping_status == PING_NONE);
  108.     assert(direct != DIRECT_YES);
  109.     debug(44, 3) ("peerSelectIcpPing: %sn", storeUrl(entry));
  110.     if (!request->flags.hierarchical && direct != DIRECT_NO)
  111. return 0;
  112.     if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
  113. if (direct != DIRECT_NO)
  114.     return 0;
  115.     n = neighborsCount(request);
  116.     debug(44, 3) ("peerSelectIcpPing: counted %d neighborsn", n);
  117.     return n;
  118. }
  119. void
  120. peerSelect(request_t * request,
  121.     StoreEntry * entry,
  122.     PSC * callback,
  123.     void *callback_data)
  124. {
  125.     ps_state *psstate = xcalloc(1, sizeof(ps_state));
  126.     if (entry)
  127. debug(44, 3) ("peerSelect: %sn", storeUrl(entry));
  128.     else
  129. debug(44, 3) ("peerSelect: %sn", RequestMethodStr[request->method]);
  130.     cbdataAdd(psstate, cbdataXfree, 0);
  131.     psstate->request = requestLink(request);
  132.     psstate->entry = entry;
  133.     psstate->callback = callback;
  134.     psstate->callback_data = callback_data;
  135.     psstate->direct = DIRECT_UNKNOWN;
  136. #if USE_CACHE_DIGESTS
  137.     request->hier.peer_select_start = current_time;
  138. #endif
  139.     if (psstate->entry)
  140. storeLockObject(psstate->entry);
  141.     cbdataLock(callback_data);
  142.     peerSelectFoo(psstate);
  143. }
  144. static void
  145. peerCheckNeverDirectDone(int answer, void *data)
  146. {
  147.     ps_state *psstate = data;
  148.     psstate->acl_checklist = NULL;
  149.     debug(44, 3) ("peerCheckNeverDirectDone: %dn", answer);
  150.     psstate->never_direct = answer ? 1 : -1;
  151.     peerSelectFoo(psstate);
  152. }
  153. static void
  154. peerCheckAlwaysDirectDone(int answer, void *data)
  155. {
  156.     ps_state *psstate = data;
  157.     psstate->acl_checklist = NULL;
  158.     debug(44, 3) ("peerCheckAlwaysDirectDone: %dn", answer);
  159.     psstate->always_direct = answer ? 1 : -1;
  160.     peerSelectFoo(psstate);
  161. }
  162. static void
  163. peerSelectCallback(ps_state * psstate)
  164. {
  165.     StoreEntry *entry = psstate->entry;
  166.     FwdServer *fs = psstate->servers;
  167.     void *data = psstate->callback_data;
  168.     if (entry) {
  169. debug(44, 3) ("peerSelectCallback: %sn", storeUrl(entry));
  170. if (entry->ping_status == PING_WAITING)
  171.     eventDelete(peerPingTimeout, psstate);
  172. entry->ping_status = PING_DONE;
  173.     }
  174.     if (fs == NULL) {
  175. debug(44, 1) ("Failed to select source for '%s'n", storeUrl(entry));
  176. debug(44, 1) ("  always_direct = %dn", psstate->always_direct);
  177. debug(44, 1) ("   never_direct = %dn", psstate->never_direct);
  178. debug(44, 1) ("       timedout = %dn", psstate->ping.timedout);
  179.     }
  180.     psstate->ping.stop = current_time;
  181.     psstate->request->hier.ping = psstate->ping;
  182.     if (cbdataValid(data)) {
  183. psstate->servers = NULL;
  184. psstate->callback(fs, data);
  185.     }
  186.     cbdataUnlock(data);
  187.     peerSelectStateFree(psstate);
  188. }
  189. static int
  190. peerCheckNetdbDirect(ps_state * psstate)
  191. {
  192.     peer *p = whichPeer(&psstate->closest_parent_miss);
  193.     int myrtt;
  194.     int myhops;
  195.     if (p == NULL)
  196. return 0;
  197.     myrtt = netdbHostRtt(psstate->request->host);
  198.     debug(44, 3) ("peerCheckNetdbDirect: MY RTT = %d msecn", myrtt);
  199.     debug(44, 3) ("peerCheckNetdbDirect: closest_parent_miss RTT = %d msecn",
  200. psstate->ping.p_rtt);
  201.     if (myrtt && myrtt < psstate->ping.p_rtt)
  202. return 1;
  203.     myhops = netdbHostHops(psstate->request->host);
  204.     debug(44, 3) ("peerCheckNetdbDirect: MY hops = %dn", myhops);
  205.     debug(44, 3) ("peerCheckNetdbDirect: minimum_direct_hops = %dn",
  206. Config.minDirectHops);
  207.     if (myhops && myhops <= Config.minDirectHops)
  208. return 1;
  209.     return 0;
  210. }
  211. static void
  212. peerSelectFoo(ps_state * ps)
  213. {
  214.     StoreEntry *entry = ps->entry;
  215.     request_t *request = ps->request;
  216.     debug(44, 3) ("peerSelectFoo: '%s %s'n",
  217. RequestMethodStr[request->method],
  218. request->host);
  219.     if (ps->direct == DIRECT_UNKNOWN) {
  220. if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
  221.     ps->acl_checklist = aclChecklistCreate(
  222. Config.accessList.AlwaysDirect,
  223. request,
  224. request->client_addr,
  225. request->my_addr,
  226. NULL, /* user agent */
  227. NULL); /* ident */
  228.     aclNBCheck(ps->acl_checklist,
  229. peerCheckAlwaysDirectDone,
  230. ps);
  231.     return;
  232. } else if (ps->always_direct > 0) {
  233.     ps->direct = DIRECT_YES;
  234. } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
  235.     ps->acl_checklist = aclChecklistCreate(
  236. Config.accessList.NeverDirect,
  237. request,
  238. request->client_addr,
  239. request->my_addr,
  240. NULL, /* user agent */
  241. NULL); /* ident */
  242.     aclNBCheck(ps->acl_checklist,
  243. peerCheckNeverDirectDone,
  244. ps);
  245.     return;
  246. } else if (ps->never_direct > 0) {
  247.     ps->direct = DIRECT_NO;
  248. } else if (request->flags.loopdetect) {
  249.     ps->direct = DIRECT_YES;
  250. } else {
  251.     ps->direct = DIRECT_MAYBE;
  252. }
  253. debug(44, 3) ("peerSelectFoo: direct = %sn",
  254.     DirectStr[ps->direct]);
  255.     }
  256.     if (entry == NULL) {
  257. (void) 0;
  258.     } else if (entry->ping_status == PING_NONE) {
  259. peerGetSomeNeighbor(ps);
  260. if (entry->ping_status == PING_WAITING)
  261.     return;
  262.     } else if (entry->ping_status == PING_WAITING) {
  263. peerGetSomeNeighborReplies(ps);
  264. entry->ping_status = PING_DONE;
  265.     }
  266.     if (Config.onoff.prefer_direct)
  267. peerGetSomeDirect(ps);
  268.     peerGetSomeParent(ps);
  269.     if (!Config.onoff.prefer_direct)
  270. peerGetSomeDirect(ps);
  271.     peerSelectCallback(ps);
  272. }
  273. /*
  274.  * peerGetSomeNeighbor
  275.  * 
  276.  * Selects a neighbor (parent or sibling) based on one of the
  277.  * following methods:
  278.  *      Cache Digests
  279.  *      CARP
  280.  *      Netdb RTT estimates
  281.  *      ICP/HTCP queries
  282.  */
  283. static void
  284. peerGetSomeNeighbor(ps_state * ps)
  285. {
  286.     StoreEntry *entry = ps->entry;
  287.     request_t *request = ps->request;
  288.     peer *p;
  289.     hier_code code = HIER_NONE;
  290.     assert(entry->ping_status == PING_NONE);
  291.     if (ps->direct == DIRECT_YES) {
  292. entry->ping_status = PING_DONE;
  293. return;
  294.     }
  295. #if USE_CACHE_DIGESTS
  296.     if ((p = neighborsDigestSelect(request, entry))) {
  297. code = CACHE_DIGEST_HIT;
  298.     } else
  299. #endif
  300. #if USE_CARP
  301.     if ((p = carpSelectParent(request))) {
  302. code = CARP;
  303.     } else
  304. #endif
  305.     if ((p = netdbClosestParent(request))) {
  306. code = CLOSEST_PARENT;
  307.     } else if (peerSelectIcpPing(request, ps->direct, entry)) {
  308. debug(44, 3) ("peerSelect: Doing ICP pingsn");
  309. ps->ping.start = current_time;
  310. ps->ping.n_sent = neighborsUdpPing(request,
  311.     entry,
  312.     peerHandlePingReply,
  313.     ps,
  314.     &ps->ping.n_replies_expected,
  315.     &ps->ping.timeout);
  316. if (ps->ping.n_sent == 0)
  317.     debug(44, 0) ("WARNING: neighborsUdpPing returned 0n");
  318. debug(44, 3) ("peerSelect: %d ICP replies expected, RTT %d msecn",
  319.     ps->ping.n_replies_expected, ps->ping.timeout);
  320. if (ps->ping.n_replies_expected > 0) {
  321.     entry->ping_status = PING_WAITING;
  322.     eventAdd("peerPingTimeout",
  323. peerPingTimeout,
  324. ps,
  325. 0.001 * ps->ping.timeout,
  326. 0);
  327.     return;
  328. }
  329.     }
  330.     if (code != HIER_NONE) {
  331. assert(p);
  332. debug(44, 3) ("peerSelect: %s/%sn", hier_strings[code], p->host);
  333. peerAddFwdServer(&ps->servers, p, code);
  334.     }
  335.     entry->ping_status = PING_DONE;
  336. }
  337. /*
  338.  * peerGetSomeNeighborReplies
  339.  * 
  340.  * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
  341.  */
  342. static void
  343. peerGetSomeNeighborReplies(ps_state * ps)
  344. {
  345.     StoreEntry *entry = ps->entry;
  346.     request_t *request = ps->request;
  347.     peer *p = NULL;
  348.     hier_code code = HIER_NONE;
  349.     assert(entry->ping_status == PING_WAITING);
  350.     assert(ps->direct != DIRECT_YES);
  351.     if (peerCheckNetdbDirect(ps)) {
  352. code = CLOSEST_DIRECT;
  353. debug(44, 3) ("peerSelect: %s/%sn", hier_strings[code], request->host);
  354. peerAddFwdServer(&ps->servers, NULL, code);
  355. return;
  356.     }
  357.     if ((p = ps->hit)) {
  358. code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
  359.     } else
  360. #if ALLOW_SOURCE_PING
  361.     if ((p = ps->secho)) {
  362. code = SOURCE_FASTEST;
  363.     } else
  364. #endif
  365.     if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
  366. p = whichPeer(&ps->closest_parent_miss);
  367. code = CLOSEST_PARENT_MISS;
  368.     } else if (ps->first_parent_miss.sin_addr.s_addr != any_addr.s_addr) {
  369. p = whichPeer(&ps->first_parent_miss);
  370. code = FIRST_PARENT_MISS;
  371.     }
  372.     if (p && code != HIER_NONE) {
  373. debug(44, 3) ("peerSelect: %s/%sn", hier_strings[code], p->host);
  374. peerAddFwdServer(&ps->servers, p, code);
  375.     }
  376. }
  377. /*
  378.  * peerGetSomeDirect
  379.  * 
  380.  * Simply adds a 'direct' entry to the FwdServers list if this
  381.  * request can be forwarded directly to the origin server
  382.  */
  383. static void
  384. peerGetSomeDirect(ps_state * ps)
  385. {
  386.     if (ps->direct == DIRECT_NO)
  387. return;
  388.     if (ps->request->protocol == PROTO_WAIS)
  389. /* Its not really DIRECT, now is it? */
  390. peerAddFwdServer(&ps->servers, Config.Wais.peer, DIRECT);
  391.     else
  392. peerAddFwdServer(&ps->servers, NULL, DIRECT);
  393. }
  394. static void
  395. peerGetSomeParent(ps_state * ps)
  396. {
  397.     peer *p;
  398.     request_t *request = ps->request;
  399.     hier_code code = HIER_NONE;
  400.     debug(44, 3) ("peerGetSomeParent: %s %sn",
  401. RequestMethodStr[request->method],
  402. request->host);
  403.     if ((p = getDefaultParent(request))) {
  404. code = DEFAULT_PARENT;
  405.     } else if ((p = getRoundRobinParent(request))) {
  406. code = ROUNDROBIN_PARENT;
  407.     } else if ((p = getFirstUpParent(request))) {
  408. code = FIRSTUP_PARENT;
  409.     } else if ((p = getAnyParent(request))) {
  410. code = ANY_OLD_PARENT;
  411.     }
  412.     if (code != HIER_NONE) {
  413. debug(44, 3) ("peerSelect: %s/%sn", hier_strings[code], p->host);
  414. peerAddFwdServer(&ps->servers, p, code);
  415.     }
  416. }
  417. static void
  418. peerPingTimeout(void *data)
  419. {
  420.     ps_state *psstate = data;
  421.     StoreEntry *entry = psstate->entry;
  422.     if (entry)
  423. debug(44, 3) ("peerPingTimeout: '%s'n", storeUrl(entry));
  424.     entry->ping_status = PING_TIMEOUT;
  425.     if (!cbdataValid(psstate->callback_data)) {
  426. /* request aborted */
  427. cbdataUnlock(psstate->callback_data);
  428. peerSelectStateFree(psstate);
  429. return;
  430.     }
  431.     PeerStats.timeouts++;
  432.     psstate->ping.timedout = 1;
  433.     peerSelectFoo(psstate);
  434. }
  435. void
  436. peerSelectInit(void)
  437. {
  438.     memset(&PeerStats, '', sizeof(PeerStats));
  439.     assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
  440. }
  441. static void
  442. peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
  443. {
  444.     int rtt;
  445.     int hops;
  446.     if (Config.onoff.query_icmp) {
  447. if (header->flags & ICP_FLAG_SRC_RTT) {
  448.     rtt = header->pad & 0xFFFF;
  449.     hops = (header->pad >> 16) & 0xFFFF;
  450.     if (rtt > 0 && rtt < 0xFFFF)
  451. netdbUpdatePeer(ps->request, p, rtt, hops);
  452.     if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
  453. ps->closest_parent_miss = p->in_addr;
  454. ps->ping.p_rtt = rtt;
  455.     }
  456. }
  457.     }
  458.     /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
  459.     if (p->options.closest_only)
  460. return;
  461.     /* set FIRST_MISS if there is no CLOSEST parent */
  462.     if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
  463. return;
  464.     rtt = tvSubMsec(ps->ping.start, current_time) / p->weight;
  465.     if (ps->ping.w_rtt == 0 || rtt < ps->ping.w_rtt) {
  466. ps->first_parent_miss = p->in_addr;
  467. ps->ping.w_rtt = rtt;
  468.     }
  469. }
  470. static void
  471. peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
  472. {
  473.     ps_state *psstate = data;
  474.     icp_opcode op = header->opcode;
  475.     debug(44, 3) ("peerHandleIcpReply: %s %sn",
  476. icp_opcode_str[op],
  477. storeUrl(psstate->entry));
  478. #if USE_CACHE_DIGESTS && 0
  479.     /* do cd lookup to count false misses */
  480.     if (p && request)
  481. peerNoteDigestLookup(request, p,
  482.     peerDigestLookup(p, request, psstate->entry));
  483. #endif
  484.     psstate->ping.n_recv++;
  485.     if (op == ICP_MISS || op == ICP_DECHO) {
  486. if (type == PEER_PARENT)
  487.     peerIcpParentMiss(p, header, psstate);
  488.     } else if (op == ICP_HIT) {
  489. psstate->hit = p;
  490. psstate->hit_type = type;
  491. peerSelectFoo(psstate);
  492. return;
  493.     }
  494. #if ALLOW_SOURCE_PING
  495.     else if (op == ICP_SECHO) {
  496. psstate->secho = p;
  497. peerSelectFoo(psstate);
  498. return;
  499.     }
  500. #endif
  501.     if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
  502. return;
  503.     peerSelectFoo(psstate);
  504. }
  505. #if USE_HTCP
  506. static void
  507. peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
  508. {
  509.     ps_state *psstate = data;
  510.     request_t *request = psstate->request;
  511.     debug(44, 3) ("peerHandleIcpReply: %s %sn",
  512. htcp->hit ? "HIT" : "MISS",
  513. storeUrl(psstate->entry));
  514.     psstate->ping.n_recv++;
  515.     if (htcp->hit) {
  516. psstate->hit = p;
  517. psstate->hit_type = type;
  518. peerSelectFoo(psstate);
  519. return;
  520.     }
  521.     if (type == PEER_PARENT)
  522. peerHtcpParentMiss(p, htcp, psstate);
  523.     if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
  524. return;
  525.     peerSelectFoo(psstate);
  526. }
  527. static void
  528. peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
  529. {
  530.     int rtt;
  531.     int hops;
  532.     if (Config.onoff.query_icmp) {
  533. if (htcp->cto.rtt > 0) {
  534.     rtt = (int) htcp->cto.rtt * 1000;
  535.     hops = (int) htcp->cto.hops * 1000;
  536.     netdbUpdatePeer(ps->request, p, rtt, hops);
  537.     if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
  538. ps->closest_parent_miss = p->in_addr;
  539. ps->ping.p_rtt = rtt;
  540.     }
  541. }
  542.     }
  543.     /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
  544.     if (p->options.closest_only)
  545. return;
  546.     /* set FIRST_MISS if there is no CLOSEST parent */
  547.     if (ps->closest_parent_miss.sin_addr.s_addr != any_addr.s_addr)
  548. return;
  549.     rtt = tvSubMsec(ps->ping.start, current_time) / p->weight;
  550.     if (ps->ping.w_rtt == 0 || rtt < ps->ping.w_rtt) {
  551. ps->first_parent_miss = p->in_addr;
  552. ps->ping.w_rtt = rtt;
  553.     }
  554. }
  555. #endif
  556. static void
  557. peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
  558. {
  559.     if (proto == PROTO_ICP)
  560. peerHandleIcpReply(p, type, pingdata, data);
  561. #if USE_HTCP
  562.     else if (proto == PROTO_HTCP)
  563. peerHandleHtcpReply(p, type, pingdata, data);
  564. #endif
  565.     else
  566. debug(44, 1) ("peerHandlePingReply: unknown protocol_t %dn", (int) proto);
  567. }
  568. static void
  569. peerAddFwdServer(FwdServer ** FS, peer * p, hier_code code)
  570. {
  571.     FwdServer *fs = memAllocate(MEM_FWD_SERVER);
  572.     debug(44, 5) ("peerAddFwdServer: adding %s %sn",
  573. p ? p->host : "DIRECT",
  574. hier_strings[code]);
  575.     fs->peer = p;
  576.     fs->code = code;
  577.     cbdataLock(fs->peer);
  578.     while (*FS)
  579. FS = &(*FS)->next;
  580.     *FS = fs;
  581. }