1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
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 name of Apple Inc. ("Apple") 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 BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      dglossy.c
82**
83**  FACILITY:
84**
85**      Remote Procedure Call (RPC)
86**
87**  ABSTRACT:
88**
89**  Implementations of rpc__socket_sendmsg() (and friends) that sometimes
90**  do bad things.  This code should be portable since it is completely
91**  layered on top of the rpc__socket_ and rpc__naf_ APIs.
92**
93**
94*/
95
96#ifdef RPC_DG_LOSSY
97
98#include <dg.h>
99
100/*
101 * Stashed data from calls to rpc__socket_sendmsg() and rpc__socket_sendto().
102 */
103
104INTERNAL struct {
105    rpc_socket_t sock;
106    rpc_socket_iovec_p_t iov;
107    int iov_len;
108    rpc_addr_p_t addr;
109} sm_stash;
110
111INTERNAL int rate = 0, dbg_prnt_level = 0;
112
113typedef unsigned32 lossy_stats_t[4];
114INTERNAL struct {
115    lossy_stats_t   total;
116    lossy_stats_t   recent;     /* stats over last LOSSY_RECENT_INTERVAL xmits */
117} lossy_stats;
118
119#define LOSSY_RECENT_INTERVAL   32
120
121/* =========================================================================== */
122
123/*
124 * S E T _ L O S S Y _ P A R A M S
125 *
126 * The rate of loss is determined by the dg_lossy debug switch value.
127 * Rather than incur the expense of making the loss level completely
128 * general, we'll look specifically for the values 100, 110, 120, 130,
129 * or 140, which will indicate the percentage of packets lost/reordered.
130 * Adding 1 to any of the above values turns on the debug output.  E.g.
131 * specify 130 to lose 30% of the packets, specify 131 to lose 30% of
132 * the packets AND be told about it.
133 */
134
135INTERNAL void set_lossy_params ( void );
136
137INTERNAL void set_lossy_params( void )
138{
139    if (rate != 0)
140        return;
141
142    if (RPC_DBG(rpc_es_dbg_dg_lossy, 140))
143    {
144        rate = 5;
145        dbg_prnt_level = 141;
146    }
147    else if (RPC_DBG(rpc_es_dbg_dg_lossy, 130))
148    {
149        rate = 6;
150        dbg_prnt_level = 131;
151    }
152    else if (RPC_DBG(rpc_es_dbg_dg_lossy, 120))
153    {
154        rate = 10;
155        dbg_prnt_level = 121;
156    }
157    else if (RPC_DBG(rpc_es_dbg_dg_lossy, 110))
158    {
159        rate = 20;
160        dbg_prnt_level = 111;
161    }
162    else
163    {
164        /*
165         * Anything below 10% gets set as 1%.
166         */
167        rate = 200;
168        dbg_prnt_level = 102;
169    }
170}
171
172/*
173 * M C O P Y
174 *
175 * Make a copy of a thing into malloc'd storage.
176 */
177
178INTERNAL unsigned8 *mcopy (
179        unsigned8 * /*p*/,
180        int  /*len*/
181    );
182
183INTERNAL unsigned8 *mcopy
184(
185    unsigned8 *p,
186    int len
187)
188{
189    unsigned8 *q;
190
191    RPC_MEM_ALLOC(q, unsigned8 *, len, RPC_C_MEM_DG_LOSSY, RPC_C_MEM_NOWAIT);
192
193    /*b_c_o_p_y(p, q, len);*/
194
195    memmove(q, p, len) ;
196
197    return (q);
198}
199
200/*
201 * M C O P Y _ I O V
202 *
203 * Make a copy of an I/O vector and all it points to into malloc'd storage.
204 */
205
206INTERNAL rpc_socket_iovec_p_t mcopy_iov (
207        rpc_socket_iovec_p_t  /*iov*/,
208        int  /*iovlen*/
209    );
210
211INTERNAL rpc_socket_iovec_p_t mcopy_iov
212(
213    rpc_socket_iovec_p_t iov,
214    int iovlen
215)
216{
217    unsigned16 i;
218    rpc_socket_iovec_p_t ciov;
219
220    RPC_MEM_ALLOC(ciov, rpc_socket_iovec_p_t, sizeof(rpc_socket_iovec_t) * iovlen,
221        RPC_C_MEM_DG_LOSSY, RPC_C_MEM_NOWAIT);
222
223    for (i = 0; i < iovlen; i++) {
224        ciov[i].base = mcopy(iov[i].base, iov[i].len);
225        ciov[i].len  = iov[i].len;
226    }
227
228    return (ciov);
229}
230
231/*
232 * F R E E _ I O V
233 *
234 * Free an I/O vector.
235 */
236
237INTERNAL void free_iov (
238        rpc_socket_iovec_p_t /*iov*/,
239        int /*iovlen*/
240    );
241
242INTERNAL void free_iov
243(
244    rpc_socket_iovec_p_t iov,
245    int iovlen
246)
247{
248    unsigned16 i;
249
250    for (i = 0; i < iovlen; i++)
251        RPC_MEM_FREE(iov[i].base, RPC_C_MEM_DG_LOSSY);
252
253    RPC_MEM_FREE(iov, RPC_C_MEM_DG_LOSSY);
254}
255
256/*
257 * S T A S H _ S E N D M S G _ P K T
258 *
259 * Make a copy and stash a packet described by rpc__socket_sendmsg() args.
260 */
261
262INTERNAL void stash_sendmsg_pkt (
263        rpc_socket_t  /*sock*/,
264        rpc_socket_iovec_p_t  /*iov*/,
265        int  /*iov_len*/,
266        rpc_addr_p_t  /*addr*/
267    );
268
269INTERNAL void stash_sendmsg_pkt
270(
271    rpc_socket_t sock,
272    rpc_socket_iovec_p_t iov,
273    int iov_len,
274    rpc_addr_p_t addr
275)
276{
277    unsigned32 st;
278
279    if (sm_stash.addr != NULL) {
280        rpc__naf_addr_free(&sm_stash.addr, &st);
281        free_iov(sm_stash.iov, sm_stash.iov_len);
282    }
283
284    sm_stash.sock       = sock;
285    sm_stash.iov        = mcopy_iov(iov, iov_len);
286    sm_stash.iov_len    = iov_len;
287    rpc__naf_addr_copy(addr, &sm_stash.addr, &st);
288}
289
290/*
291 * X M I T _ S T A S H E D _ S E N D M S G _ P K T
292 *
293 * Transmit the packet we stashed away earlier.
294 */
295
296INTERNAL void xmit_stashed_sendmsg_pkt (void);
297
298INTERNAL void xmit_stashed_sendmsg_pkt( void )
299{
300    int cc;
301    rpc_socket_error_t serr;
302
303    if (sm_stash.addr == NULL)
304        return;
305
306    RPC_DBG_PRINTF(rpc_e_dbg_general, dbg_prnt_level,
307        ("(xmit_stashed_sendmsg_pkt) Re-xmit\n"));
308
309    serr = rpc__socket_sendmsg(sm_stash.sock, sm_stash.iov, sm_stash.iov_len,
310                                sm_stash.addr, &cc);
311    if (! RPC_SOCKET_IS_ERR(serr))
312    {
313        RPC_DG_PLOG_LOSSY_SENDMSG_PKT(sm_stash.iov, sm_stash.iov_len, 2);
314    }
315}
316
317/* =========================================================================== */
318
319/*
320 * R P C _ _ D G _ L O S S Y _ S O C K E T _ S E N D M S G
321 *
322 * Version of rpc__socket_sendmsg() that sometimes loses, duplicates,
323 * or re-orders delivery of packets.  The rate of loss is determined
324 * by the dg_lossy debug switch value (see set_lossy_params()).
325 */
326
327PRIVATE rpc_socket_error_t rpc__dg_lossy_socket_sendmsg
328(
329    rpc_socket_t sock,
330    rpc_socket_iovec_p_t iov,   /* array of bufs of data to send */
331    int iov_len,                /* number of bufs */
332    rpc_addr_p_t addr,          /* addr of receiver */
333    int *cc                    /* returned number of bytes actually sent */
334)
335{
336    int i;
337    rpc_socket_error_t serr;
338
339    /*
340     * Iff the lossy switch has been set...
341     */
342
343    if (RPC_DBG(rpc_es_dbg_dg_lossy, 100))
344    {
345        set_lossy_params();
346        if (lossy_stats.total[3] % LOSSY_RECENT_INTERVAL == 0)
347        {
348            lossy_stats.recent[0] = lossy_stats.recent[1] =
349                lossy_stats.recent[2] = lossy_stats.recent[3] = 0;
350        }
351        lossy_stats.recent[3]++;
352        lossy_stats.total[3]++;
353        switch ((int) RPC_RANDOM_GET(0, 10000) % rate)  /* Min/Max values arbitrarily set */
354        {
355            case 0:                         /* Drop the pkt on the floor */
356                RPC_DBG_PRINTF(rpc_e_dbg_general, dbg_prnt_level,
357                    ("(rpc__dg_lossy_socket_sendmsg) Drop pkt\n"));
358                lossy_stats.recent[0]++;
359                lossy_stats.total[0]++;
360                *cc = 0;
361                for (i = 0; i < iov_len; i++)
362                    *cc += iov[i].len;
363                RPC_DG_PLOG_LOSSY_SENDMSG_PKT(iov, iov_len, 0);
364                return (RPC_C_SOCKET_OK);
365            case 1:                         /* Stash the pkt away for later re-xmit */
366                RPC_DBG_PRINTF(rpc_e_dbg_general, dbg_prnt_level,
367                    ("(rpc__dg_lossy_socket_sendmsg) Stash pkt\n"));
368                lossy_stats.recent[1]++;
369                lossy_stats.total[1]++;
370                stash_sendmsg_pkt(sock, iov, iov_len, addr);
371                break;
372            case 2:                         /* Re-xmit sendto stashed pkt if we have one */
373                lossy_stats.recent[2]++;
374                lossy_stats.total[2]++;
375                xmit_stashed_sendmsg_pkt();
376                break;
377        }
378    }
379
380    serr = rpc__socket_sendmsg(sock, iov, iov_len, addr, cc);
381    if (! RPC_SOCKET_IS_ERR(serr))
382    {
383        RPC_DG_PLOG_LOSSY_SENDMSG_PKT(iov, iov_len, 3);
384    }
385    return (serr);
386}
387
388#else
389/*
390 * If RPC_DG_LOSSY is not defined, declare a dummy variable to
391 * enable this module to be compiled under strict ansi c standards.
392 */
393#ifndef __GNUC__
394static  char dummy, *_dummy_addr = &dummy;
395#endif
396#endif /* RPC_DG_LOSSY */
397