1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * 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 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31 char link_passthru_c_cvsid[] = "$Id: link_passthru.c,v 1.14 2006-02-28 21:08:13 thanos Exp $";
32
33 #include "liblink_i.h"
34
35 /*
36 * To get the queueing/back-pressure semantics right, we need to keep
37 * our own packet queue here in passthru. When a writer from above hands
38 * us a packet, we queue it to allow for the case where more than one
39 * output packet is generated from a single input packet. the output
40 * packets are then fed to the underlying link device as quickly as the
41 * device will take them. (We set the input queue length of our
42 * underlying device to be quite small.) We unblock to receive the next
43 * message after the queue drains.
44 */
45
46 QUEUE_INST(link_pass,_,packets,packet_t,link_pass_ctx_t);
47
48 int link_pass_is_blocked(link_pass_ctx_t *ctx)
49 {
50 // ctx->lower can be null and this leads to segfaults...
51 if (ctx->lower==NULL) {
52 return 0;
53 }
54
55 return (lu_writable_cb_get_enable(ctx->lower) ||
56 ctx->blocking_requested || ctx->withhold_unblock ||
57 link_pass_top(ctx)) ? 1 : 0;
58 }
59
60 /*
61 * unblock the upper if
62 * - the server did not request blocking
63 * - the writable handler is not pending
64 * - we are not temporarily withholding blocking
65 * - the internal queue is empty
66 */
67
68 static
69 void link_pass_maybe_unblock(link_pass_ctx_t *ctx)
70 {
71 if (!link_pass_is_blocked(ctx))
72 lp_unblock(ctx->upper, 0);
73 }
74
75
76 /*
77 * enables the client to request blocking..
78 */
79
80 void link_pass_block(link_pass_ctx_t *ctx)
81 {
82 ctx->blocking_requested = 1;
83 }
84
85
86 void link_pass_unblock(link_pass_ctx_t *ctx)
87 {
88 ctx->blocking_requested = 0;
89 link_pass_maybe_unblock(ctx);
90 }
91
92
93 int link_pass_blocking_requested(link_pass_ctx_t *ctx)
94 {
95 return ctx->blocking_requested ? 1 : 0;
96 }
97
98
99 /*
100 * This callback is called whenever a packet arrives up from the
101 * underlying datalink interfaces
102 */
103 static
104 int link_pass_receiver(lu_context_t *lu, link_pkt_t *link_pkt, ssize_t data_len)
105
106 {
107 link_pass_ctx_t *lp = (link_pass_ctx_t *) lu_data(lu);
108
109 /* call our handler */
110 if (lp->opts.pkt_from_lower)
111 lp->opts.pkt_from_lower(lp, link_pkt, data_len);
112
113 free(link_pkt);
114 return EVENT_RENEW;
115 }
116
117
118 /* receipt forwarder .. */
119 static
120 int link_pass_receipt_handler(lu_context_t *lu, link_pkt_t *link_pkt, ssize_t data_len)
121
122 {
123 link_pass_ctx_t *lp = (link_pass_ctx_t *) lu_data(lu);
124
125 /* if it's a receipt, just pass it up */
126 if (link_pkt->ext_type == MAC_CTRL_RECEIPT) {
127 lp_receive(lp->upper, link_pkt, data_len);
128 }
129
130 free(link_pkt);
131 return EVENT_RENEW;
132 }
133
134
135 static
136 int link_pass_do_send(link_pass_ctx_t *ctx)
137 {
138 int retval;
139 buf_t *next;
140 packet_t *top;
141
142 repeat:
143 /* stop now if our writable callback is pending.. */
144 if (lu_writable_cb_get_enable(ctx->lower))
145 return LP_BLOCKED;
146
147 /* grab the top */
148 top = link_pass_top(ctx);
149
150 /* if there's nothing to write, maybe unblock and return 0 */
151 if (top == NULL) {
152 link_pass_maybe_unblock(ctx);
153 return 0;
154 }
155
156 /* ok, try to send */
157 next = top->buf;
158 retval = lu_send(ctx->lower, (link_pkt_t *)next->buf,
159 next->len - sizeof(link_pkt_t));
160
161 if (retval < 0) {
162 if (errno == EAGAIN) {
163 lu_writable_cb_set_enable(ctx->lower, 1);
164 return LP_BLOCKED;
165 }
166 }
167
168 /* succeeded.. pop and continue */
169 link_pass_pop(ctx);
170 buf_free(next);
171 free(top);
172 goto repeat;
173 }
174
175
176 int link_pass_to_lower(link_pass_ctx_t *ctx, buf_t *buf)
177 {
178 /* queue it! */
179 packet_t *next = g_new0(packet_t, 1);
180 next->buf = buf;
181 link_pass_push(ctx, next);
182 return link_pass_do_send(ctx);
183 }
184
185
186 int link_pass_to_upper(link_pass_ctx_t *ctx, buf_t *buf)
187 {
188 int retval = -1;
189
190 if (ctx) {
191 lp_receive(ctx->upper, (link_pkt_t*)buf->buf, buf->len-sizeof(link_pkt_t));
192 retval = 0;
193 }
194
195 buf_free(buf);
196 return retval;
197 }
198
199
200 static
201 int link_pass_writable(lu_context_t *lu)
202 {
203 link_pass_ctx_t *ctx = (link_pass_ctx_t *) lu_data(lu);
204
205 /* we're writable now.. */
206 lu_writable_cb_set_enable(lu, 0);
207
208 /* try to send some more -- will reactivate handler if needed */
209 link_pass_do_send(ctx);
210
211 return EVENT_RENEW;
212 }
213
214
215 /*
216 * configure lower link when link is opened..
217 */
218 static
219 int link_pass_configure(lu_context_t *lu)
220 {
221 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lu_data(lu);
222 lu_set_promisc(lu, lp_get_promisc_state(ctx->upper));
223
224 if (ctx->opts.configure_lower)
225 ctx->opts.configure_lower(ctx);
226
227 return EVENT_RENEW;
228 }
229
230
231 /*
232 * Callback when the queue of packets written to our local device file
233 * is serviced
234 */
235 static
236 int link_pass_send(lp_context_t *lp, link_pkt_t *link_pkt,
237 int data_len, int loop_needed)
238 {
239 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
240
241 /* call our handler */
242 if (ctx->opts.pkt_from_upper) {
243
244 /*
245 * sending normally will trigger unblocking if the send succeeds.
246 * however, it may be the case that the client is planning to
247 * block at the "application level". so we delay the "maybe_unblock"
248 * until after the client's call returns
249 */
250
251 ctx->withhold_unblock = 1;
252 if (ctx->opts.pkt_from_upper(ctx, link_pkt, data_len) == LP_BLOCKED)
253 link_pass_block(ctx);
254 ctx->withhold_unblock = 0;
255
256 link_pass_maybe_unblock(ctx);
257 }
258
259 /* loop if needed */
260 if (loop_needed) {
261 struct timeval now;
262 gettimeofday(&now, NULL);
263 link_pkt->rcv_time = now;
264 link_pkt->src.id = lp->status.if_id;
265 lp_loop_receive(lp, link_pkt, data_len);
266 }
267
268 /* block if there is data waiting.. */
269 return link_pass_is_blocked(ctx) ? LP_BLOCKED : 0;
270 }
271
272 static
273 int link_pass_enqueue(lp_context_t *lp, link_pkt_t *link_pkt,
274 int data_len)
275 {
276 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
277 int retval = 0;
278
279 /* call our handler */
280 if (ctx->opts.enqueue_pkt_from_upper)
281 retval = ctx->opts.enqueue_pkt_from_upper(ctx, link_pkt, data_len);
282
283 return retval;
284 }
285
286
287 /* pass down promiscuous mode */
288 static
289 void link_pass_promisc(lp_context_t *lp, int mode)
290 {
291 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
292 lu_set_promisc(ctx->lower, mode);
293 }
294
295
296 /*
297 * proxy ioctl downwards
298 */
299 static int link_pass_ioctl(lp_context_t *lp, int cmd, void *arg)
300 {
301 link_pass_ctx_t *ctx = (link_pass_ctx_t *) lp_data(lp);
302 int retval = lu_ioctl(ctx->lower, cmd, arg);
303 if (retval >= 0) return retval;
304 else return -errno;
305 }
306
307
308 /*
309 * status device handling / providing
310 */
311
312 static
313 int link_pass_status_notify(lu_context_t *lu)
314 {
315 link_pass_ctx_t *ctx = (link_pass_ctx_t *) lu_data(lu);
316
317 if (ctx->opts.status_notify)
318 ctx->opts.status_notify(ctx);
319
320 else {
321 /* if no callback specified, do mtu-adjust */
322 link_status_t new_stat;
323 if (lu_status(lu, &new_stat) < 0) {
324 elog(LOG_WARNING, "Can't get status?");
325 goto out;
326 }
327
328 /* handle common cases */
329 if (ctx->opts.mtu_adjust)
330 new_stat.MTU += ctx->opts.mtu_adjust;
331 else if (ctx->opts.new_fixed_mtu)
332 new_stat.MTU = ctx->opts.new_fixed_mtu;
333
334 link_pass_push_status_to_upper(ctx, &new_stat);
335 }
336
337 out:
338 return EVENT_RENEW;
339 }
340
341
342 void link_pass_push_status_to_upper(link_pass_ctx_t *ctx, link_status_t *stat)
343 {
344 if (ctx && stat) lp_push_status(ctx->upper, stat);
345 }
346
347
348 int link_pass_get_lower_status(link_pass_ctx_t *ctx, link_status_t *stat)
349 {
350 return (ctx && ctx->lower) ? lu_status(ctx->lower, stat) : -1;
351 }
352
353
354 void link_pass_abort_queue(link_pass_ctx_t *ctx)
355 {
356 packet_t *p;
357
358 /* pop & clear */
359 while ((p = link_pass_pop(ctx))) {
360 buf_free(p->buf);
361 free(p);
362 }
363
364 /* maybe unblock */
365 link_pass_maybe_unblock(ctx);
366 }
367
368
369 static
370 int link_pass_command(lp_context_t *lp, parser_state_t *cmd_input)
371 {
372 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
373 buf_t *buf = buf_new();
374 int retval = EVENT_RENEW;
375
376 if (ctx->opts.process_command)
377 ctx->opts.process_command(ctx, cmd_input, buf);
378 else
379 bufcpy(buf, cmd_input->input, strlen(cmd_input->input));
380
381 if (buf->len > 0) {
382 if (write_to_file(link_name(&(ctx->opts.uses), LINK_COMMAND_SUBDEV), buf->buf) < 0)
383 retval = EVENT_ERROR(errno);
384 }
385
386 buf_free(buf);
387 return retval;
388 }
389
390
391 static
392 void link_pass_usage(lp_context_t *lp, buf_t *fill_usage)
393 {
394 link_pass_ctx_t *ctx = (link_pass_ctx_t *)lp_data(lp);
395 if (ctx->opts.usage)
396 ctx->opts.usage(ctx, fill_usage);
397 file_to_buf(fill_usage, link_name(&(ctx->opts.uses), LINK_COMMAND_SUBDEV));
398 }
399
400
401 int link_pass_new(link_pass_opts_t *opts, link_pass_ctx_t **ref)
402 {
403 int retval = 0;
404
405 /* link device state */
406 link_pass_ctx_t *lp = g_new0(link_pass_ctx_t, 1);
407
408 /* make sure we allocated this sucker */
409 if (lp == NULL) {
410 retval = -ENOMEM;
411 goto done;
412 }
413
414 /* make sure the options are valid */
415 if (opts == NULL) {
416 elog(LOG_ERR, "invalid link options (null)");
417 retval = -EINVAL;
418 goto done;
419 }
420
421 /* store ref */
422 lp->ref = ref;
423
424 /* copy the options struct */
425 lp->opts = *opts;
426
427 /* don't need to copy the names.. underlying events expose copies */
428
429 /* register above */
430 {
431 /* set options for the link provider interface */
432 lp_opts_t lp_opts = {
433 description: opts->description,
434 send: link_pass_send,
435 enqueue: link_pass_enqueue,
436 ioctl: link_pass_ioctl,
437 promisc_mode: link_pass_promisc,
438 opts: opts->provides,
439 command_request: link_pass_command,
440 usage: link_pass_usage
441 };
442
443 /* store data pointer */
444 lp_opts.opts.data = lp;
445
446 /* try to register the packetdev as a link interface */
447 if (lp_register(&lp_opts, &(lp->upper)) < 0) {
448 elog(LOG_CRIT, "can't create link device %s: %m",
449 link_name(&opts->provides, NULL));
450 retval = -1;
451 goto done;
452 }
453 }
454
455 /* open below */
456 {
457 lu_opts_t lu_opts = {
458 opts: opts->uses,
459 receive: link_pass_receiver,
460 writable: link_pass_writable,
461 configure: link_pass_configure,
462 status_notify: link_pass_status_notify,
463 };
464
465 /* swap data pointers */
466 lp->data = opts->uses.data;
467 lu_opts.opts.data = lp;
468
469 /* try to open the lower link interface */
470 if (lu_open(&lu_opts, &(lp->lower)) < 0) {
471 elog(LOG_CRIT, "can't open link device %s: %m",
472 link_name(&opts->uses, NULL));
473 retval = -1;
474 goto done;
475 }
476
477 if (lu_opts.opts.pkt_type && !opts->dont_auto_forward_receipts) {
478
479 /* register additional event to receive receipts only */
480 lu_opts.opts.pkt_type = PKT_TYPE_MAC_CTRL;
481 lu_opts.receive = link_pass_receipt_handler;
482 lu_opts.writable = NULL;
483 lu_opts.configure = NULL;
484 lu_opts.status_notify = NULL;
485
486 if (lu_open(&lu_opts, &(lp->lower_receipts)) < 0) {
487 elog(LOG_CRIT, "can't open link device %s: %m",
488 link_name(&opts->uses, NULL));
489 retval = -1;
490 goto done;
491 }
492
493 }
494 }
495
496 done:
497
498 /* if we've had an error, close and dealloc */
499 if (retval < 0) {
500 if (lp) {
501 link_pass_destroy(lp);
502 lp = NULL;
503 }
504 }
505
506 /* write the link provider context (or, a NULL link provider context
507 pointer) to the user's provided reference, if any */
508 if (ref) {
509 if (*ref) {
510 elog(LOG_WARNING, "Destroying link dev before reusing reference");
511 link_pass_destroy(*ref);
512 }
513 *ref = lp;
514 }
515
516 /* return either success or failure with errno properly set */
517 if (retval < 0) {
518 errno = -retval;
519 return -1;
520 } else {
521 errno = 0;
522 return 0;
523 }
524 }
525
526
527 void link_pass_destroy(link_pass_ctx_t *lpt)
528 {
529 if (lpt) {
530 if (lpt->ref)
531 *(lpt->ref) = NULL;
532
533 lp_destroy(lpt->upper);
534 lu_destroy(lpt->lower);
535 link_pass_abort_queue(lpt);
536 g_free(lpt);
537 }
538 }
539
540
541 void *link_pass_data(link_pass_ctx_t *ctx)
542 {
543 return ctx ? ctx->data : NULL;
544 }
545
546
547 char *link_pass_name(link_pass_ctx_t *ctx, char *suffix)
548 {
549 return ctx ? lp_name(ctx->upper, suffix) : NULL;
550 }
551
552
553 lu_context_t *link_pass_get_lower(link_pass_ctx_t *ctx)
554 {
555 return ctx ? ctx->lower : NULL;
556 }
557
558
559 lp_context_t *link_pass_get_upper(link_pass_ctx_t *ctx)
560 {
561 return ctx ? ctx->upper : NULL;
562 }
563
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.