1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005-2011 Daniel Braniss <[email protected]>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 /*
32  | $Id: iscsivar.h 743 2009-08-08 10:54:53Z danny $
33  */
34 #define ISCSI_MAX_LUNS		128	// don't touch this
35 #if ISCSI_MAX_LUNS > 8
36 /*
37  | for this to work
38  | sysctl kern.cam.cam_srch_hi=1
39  */
40 #endif
41 
42 #ifndef ISCSI_INITIATOR_DEBUG
43 #define ISCSI_INITIATOR_DEBUG 1
44 #endif
45 
46 #ifdef ISCSI_INITIATOR_DEBUG
47 extern int iscsi_debug;
48 #define debug(level, fmt, args...)	do {if(level <= iscsi_debug)\
49 	printf("%s: " fmt "\n", __func__ , ##args);} while(0)
50 #define sdebug(level, fmt, args...)	do {if(level <= iscsi_debug)\
51      	printf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0)
52 #define debug_called(level)		do {if(level <= iscsi_debug)\
53 	printf("%s: called\n",  __func__);} while(0)
54 #else
55 #define debug(level, fmt, args...)
56 #define debug_called(level)
57 #define sdebug(level, fmt, args...)
58 #endif /* ISCSI_INITIATOR_DEBUG */
59 
60 #define xdebug(fmt, args...)	printf(">>> %s: " fmt "\n", __func__ , ##args)
61 
62 #define MAX_SESSIONS	ISCSI_MAX_TARGETS
63 #define MAX_PDUS	(MAX_SESSIONS*256) // XXX: at the moment this is arbitrary
64 
65 typedef uint32_t digest_t(const void *, int len, uint32_t ocrc);
66 
67 MALLOC_DECLARE(M_ISCSI);
68 MALLOC_DECLARE(M_ISCSIBUF);
69 
70 #define ISOK2DIG(dig, pp)	((dig != NULL) && ((pp->ipdu.bhs.opcode & 0x1f) != ISCSI_LOGIN_CMD))
71 
72 #ifndef BIT
73 #define BIT(n)	(1 <<(n))
74 #endif
75 
76 #define ISC_SM_RUN	BIT(0)
77 #define ISC_SM_RUNNING	BIT(1)
78 
79 #define ISC_LINK_UP	BIT(2)
80 #define ISC_CON_RUN	BIT(3)
81 #define ISC_CON_RUNNING	BIT(4)
82 #define ISC_KILL	BIT(5)
83 #define ISC_OQNOTEMPTY	BIT(6)
84 #define ISC_OWAITING	BIT(7)
85 #define ISC_FFPHASE	BIT(8)
86 
87 #define ISC_CAMDEVS	BIT(9)
88 #define ISC_SCANWAIT	BIT(10)
89 
90 #define ISC_MEMWAIT	BIT(11)
91 #define ISC_SIGNALED	BIT(12)
92 
93 #define ISC_HOLD	BIT(15)
94 #define ISC_HOLDED	BIT(16)
95 
96 #define ISC_SHUTDOWN	BIT(31)
97 
98 /*
99  | some stats
100  */
101 struct i_stats {
102      int	npdu;	// number of pdus malloc'ed.
103      int	nrecv;	// unprocessed received pdus
104      int	nsent;	// sent pdus
105 
106      int	nrsp, max_rsp;
107      int	nrsv, max_rsv;
108      int	ncsnd, max_csnd;
109      int	nisnd, max_isnd;
110      int	nwsnd, max_wsnd;
111      int	nhld, max_hld;
112 
113      struct bintime t_sent;
114      struct bintime t_recv;
115 };
116 
117 /*
118  | one per 'session'
119  */
120 
121 typedef TAILQ_HEAD(, pduq) queue_t;
122 
123 typedef struct isc_session {
124      TAILQ_ENTRY(isc_session)	sp_link;
125      int		flags;
126      struct cdev	*dev;
127      struct socket	*soc;
128      struct file	*fp;
129      struct thread	*td;
130 
131      struct proc 	*proc; // the userland process
132      int		signal;
133      struct proc 	*soc_proc;
134      struct proc	*stp;	// the sm thread
135 
136      struct isc_softc	*isc;
137 
138      digest_t   	*hdrDigest;     // the digest alg. if any
139      digest_t   	*dataDigest;    // the digest alg. if any
140 
141      int		sid;		// Session ID
142      sn_t       	sn;             // sequence number stuff;
143      int		cws;		// current window size
144 
145      int		target_nluns; // this and target_lun are
146 				      // hopefully temporal till I
147 				      // figure out a better way.
148      int		target_lun[ISCSI_MAX_LUNS/(sizeof(int)*8) + 1];
149 
150      struct mtx		rsp_mtx;
151      struct mtx		rsv_mtx;
152      struct mtx		snd_mtx;
153      struct mtx		hld_mtx;
154      struct mtx		io_mtx;
155      queue_t		rsp;
156      queue_t		rsv;
157      queue_t		csnd;
158      queue_t		isnd;
159      queue_t		wsnd;
160      queue_t		hld;
161 
162      isc_opt_t		opt;	// negotiable values
163 
164      struct i_stats	stats;
165      bhs_t		bhs;
166      struct uio		uio;
167      struct iovec	iov;
168      /*
169       | cam stuff
170       */
171      struct cam_sim	*cam_sim;
172      struct cam_path	*cam_path;
173      struct mtx		cam_mtx;
174      /*
175       | sysctl stuff
176       */
177      struct sysctl_ctx_list	clist;
178      struct sysctl_oid	*oid;
179      int	douio;	//XXX: turn on/off uio on read
180 } isc_session_t;
181 
182 typedef struct pduq {
183      TAILQ_ENTRY(pduq)	pq_link;
184 
185      caddr_t		buf;
186      u_int		len;	// the total length of the pdu
187      pdu_t		pdu;
188      union ccb		*ccb;
189 
190      struct uio		uio;
191      struct iovec	iov[5];	// XXX: careful ...
192      struct mbuf	*mp;
193      struct bintime	ts;
194      queue_t		*pduq;
195 } pduq_t;
196 /*
197  */
198 struct isc_softc {
199      struct mtx		isc_mtx;
200      TAILQ_HEAD(,isc_session)	isc_sess;
201      int		nsess;
202      struct cdev	*dev;
203      char		isid[6];	// Initiator Session ID (48 bits)
204      struct unrhdr	*unit;
205      struct sx 		unit_sx;
206 
207      uma_zone_t		pdu_zone;	// pool of free pdu's
208      TAILQ_HEAD(,pduq)	freepdu;
209 
210 #ifdef  ISCSI_INITIATOR_DEBUG
211      int		 npdu_alloc, npdu_max; // for instrumentation
212 #endif
213 #ifdef DO_EVENTHANDLER
214      eventhandler_tag	eh;
215 #endif
216      /*
217       | sysctl stuff
218       */
219      struct sysctl_ctx_list	clist;
220      struct sysctl_oid		*oid;
221 };
222 
223 #ifdef  ISCSI_INITIATOR_DEBUG
224 extern struct mtx iscsi_dbg_mtx;
225 #endif
226 
227 void	isc_start_receiver(isc_session_t *sp);
228 void	isc_stop_receiver(isc_session_t *sp);
229 
230 int	isc_sendPDU(isc_session_t *sp, pduq_t *pq);
231 int	isc_qout(isc_session_t *sp, pduq_t *pq);
232 int	i_prepPDU(isc_session_t *sp, pduq_t *pq);
233 
234 int	ism_fullfeature(struct cdev *dev, int flag);
235 
236 int	i_pdu_flush(isc_session_t *sc);
237 int	i_setopt(isc_session_t *sp, isc_opt_t *opt);
238 void	i_freeopt(isc_opt_t *opt);
239 
240 int	ic_init(isc_session_t *sp);
241 void	ic_destroy(isc_session_t *sp);
242 void	ic_lost_target(isc_session_t *sp, int target);
243 int	ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp);
244 
245 void	ism_recv(isc_session_t *sp, pduq_t *pq);
246 int	ism_start(isc_session_t *sp);
247 void	ism_restart(isc_session_t *sp);
248 void	ism_stop(isc_session_t *sp);
249 
250 int	scsi_encap(struct cam_sim *sim, union ccb *ccb);
251 int	scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
252 void	iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
253 void	iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
254 void	iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
255 void	iscsi_async(isc_session_t *sp,  pduq_t *pq);
256 void	iscsi_cleanup(isc_session_t *sp);
257 int	iscsi_requeue(isc_session_t *sp);
258 
259 // Serial Number Arithmetic
260 #define _MAXINCR	0x7FFFFFFF	// 2 ^ 31 - 1
261 #define SNA_GT(i1, i2)	((i1 != i2) && (\
262 	(i1 < i2 && i2 - i1 > _MAXINCR) ||\
263 	(i1 > i2 && i1 - i2 < _MAXINCR))?1: 0)
264 
265 /*
266  | inlines
267  */
268 #ifdef _CAM_CAM_XPT_SIM_H
269 
270 #if __FreeBSD_version <  600000
271 #define CAM_LOCK(arg)
272 #define CAM_ULOCK(arg)
273 
274 static __inline void
XPT_DONE(isc_session_t * sp,union ccb * ccb)275 XPT_DONE(isc_session_t *sp, union ccb *ccb)
276 {
277      mtx_lock(&Giant);
278      xpt_done(ccb);
279      mtx_unlock(&Giant);
280 }
281 #elif __FreeBSD_version >= 700000
282 #define CAM_LOCK(arg)	mtx_lock(&arg->cam_mtx)
283 #define CAM_UNLOCK(arg)	mtx_unlock(&arg->cam_mtx)
284 
285 static __inline void
XPT_DONE(isc_session_t * sp,union ccb * ccb)286 XPT_DONE(isc_session_t *sp, union ccb *ccb)
287 {
288      CAM_LOCK(sp);
289      xpt_done(ccb);
290      CAM_UNLOCK(sp);
291 }
292 #else
293 //__FreeBSD_version >= 600000
294 #define CAM_LOCK(arg)
295 #define CAM_UNLOCK(arg)
296 #define XPT_DONE(ignore, arg)	xpt_done(arg)
297 #endif
298 
299 #endif /* _CAM_CAM_XPT_SIM_H */
300 
301 static __inline pduq_t *
pdu_alloc(struct isc_softc * isc,int wait)302 pdu_alloc(struct isc_softc *isc, int wait)
303 {
304      pduq_t	*pq;
305 
306      pq = (pduq_t *)uma_zalloc(isc->pdu_zone, wait /* M_WAITOK or M_NOWAIT*/);
307      if(pq == NULL) {
308 	  debug(7, "out of mem");
309 	  return NULL;
310      }
311 #ifdef ISCSI_INITIATOR_DEBUG
312      mtx_lock(&iscsi_dbg_mtx);
313      isc->npdu_alloc++;
314      if(isc->npdu_alloc > isc->npdu_max)
315 	  isc->npdu_max = isc->npdu_alloc;
316      mtx_unlock(&iscsi_dbg_mtx);
317 #endif
318      memset(pq, 0, sizeof(pduq_t));
319 
320      return pq;
321 }
322 
323 static __inline void
pdu_free(struct isc_softc * isc,pduq_t * pq)324 pdu_free(struct isc_softc *isc, pduq_t *pq)
325 {
326      if(pq->mp)
327 	  m_freem(pq->mp);
328 #ifdef NO_USE_MBUF
329      if(pq->buf != NULL)
330 	  free(pq->buf, M_ISCSIBUF);
331 #endif
332      uma_zfree(isc->pdu_zone, pq);
333 #ifdef ISCSI_INITIATOR_DEBUG
334      mtx_lock(&iscsi_dbg_mtx);
335      isc->npdu_alloc--;
336      mtx_unlock(&iscsi_dbg_mtx);
337 #endif
338 }
339 
340 static __inline void
i_nqueue_rsp(isc_session_t * sp,pduq_t * pq)341 i_nqueue_rsp(isc_session_t *sp, pduq_t *pq)
342 {
343      mtx_lock(&sp->rsp_mtx);
344      if(++sp->stats.nrsp > sp->stats.max_rsp)
345 	  sp->stats.max_rsp = sp->stats.nrsp;
346      TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link);
347      mtx_unlock(&sp->rsp_mtx);
348 }
349 
350 static __inline pduq_t *
i_dqueue_rsp(isc_session_t * sp)351 i_dqueue_rsp(isc_session_t *sp)
352 {
353      pduq_t *pq;
354 
355      mtx_lock(&sp->rsp_mtx);
356      if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) {
357 	  sp->stats.nrsp--;
358 	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
359      }
360      mtx_unlock(&sp->rsp_mtx);
361 
362      return pq;
363 }
364 
365 static __inline void
i_nqueue_rsv(isc_session_t * sp,pduq_t * pq)366 i_nqueue_rsv(isc_session_t *sp, pduq_t *pq)
367 {
368      mtx_lock(&sp->rsv_mtx);
369      if(++sp->stats.nrsv > sp->stats.max_rsv)
370 	  sp->stats.max_rsv = sp->stats.nrsv;
371      TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link);
372      mtx_unlock(&sp->rsv_mtx);
373 }
374 
375 static __inline pduq_t *
i_dqueue_rsv(isc_session_t * sp)376 i_dqueue_rsv(isc_session_t *sp)
377 {
378      pduq_t *pq;
379 
380      mtx_lock(&sp->rsv_mtx);
381      if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) {
382 	  sp->stats.nrsv--;
383 	  TAILQ_REMOVE(&sp->rsv, pq, pq_link);
384      }
385      mtx_unlock(&sp->rsv_mtx);
386 
387      return pq;
388 }
389 
390 static __inline void
i_nqueue_csnd(isc_session_t * sp,pduq_t * pq)391 i_nqueue_csnd(isc_session_t *sp, pduq_t *pq)
392 {
393      mtx_lock(&sp->snd_mtx);
394      if(++sp->stats.ncsnd > sp->stats.max_csnd)
395 	  sp->stats.max_csnd = sp->stats.ncsnd;
396      TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link);
397      mtx_unlock(&sp->snd_mtx);
398 }
399 
400 static __inline pduq_t *
i_dqueue_csnd(isc_session_t * sp)401 i_dqueue_csnd(isc_session_t *sp)
402 {
403      pduq_t *pq;
404 
405      mtx_lock(&sp->snd_mtx);
406      if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
407 	  sp->stats.ncsnd--;
408 	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
409      }
410      mtx_unlock(&sp->snd_mtx);
411 
412      return pq;
413 }
414 
415 static __inline void
i_nqueue_isnd(isc_session_t * sp,pduq_t * pq)416 i_nqueue_isnd(isc_session_t *sp, pduq_t *pq)
417 {
418      mtx_lock(&sp->snd_mtx);
419      if(++sp->stats.nisnd > sp->stats.max_isnd)
420 	  sp->stats.max_isnd = sp->stats.nisnd;
421      TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link);
422      mtx_unlock(&sp->snd_mtx);
423 }
424 
425 static __inline pduq_t *
i_dqueue_isnd(isc_session_t * sp)426 i_dqueue_isnd(isc_session_t *sp)
427 {
428      pduq_t *pq;
429 
430      mtx_lock(&sp->snd_mtx);
431      if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
432 	  sp->stats.nisnd--;
433 	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
434      }
435      mtx_unlock(&sp->snd_mtx);
436 
437      return pq;
438 }
439 
440 static __inline void
i_nqueue_wsnd(isc_session_t * sp,pduq_t * pq)441 i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq)
442 {
443      mtx_lock(&sp->snd_mtx);
444      if(++sp->stats.nwsnd > sp->stats.max_wsnd)
445 	  sp->stats.max_wsnd = sp->stats.nwsnd;
446      TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link);
447      mtx_unlock(&sp->snd_mtx);
448 }
449 
450 static __inline pduq_t *
i_dqueue_wsnd(isc_session_t * sp)451 i_dqueue_wsnd(isc_session_t *sp)
452 {
453      pduq_t *pq;
454 
455      mtx_lock(&sp->snd_mtx);
456      if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
457 	  sp->stats.nwsnd--;
458 	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
459      }
460      mtx_unlock(&sp->snd_mtx);
461 
462      return pq;
463 }
464 
465 static __inline pduq_t *
i_dqueue_snd(isc_session_t * sp,int which)466 i_dqueue_snd(isc_session_t *sp, int which)
467 {
468      pduq_t *pq;
469 
470      pq = NULL;
471      mtx_lock(&sp->snd_mtx);
472      if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
473 	  sp->stats.nisnd--;
474 	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
475 	  pq->pduq = &sp->isnd;	// remember where you came from
476      } else
477      if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
478 	  sp->stats.nwsnd--;
479 	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
480 	  pq->pduq = &sp->wsnd;	// remember where you came from
481      } else
482      if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
483 	  sp->stats.ncsnd--;
484 	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
485 	  pq->pduq = &sp->csnd;	// remember where you came from
486      }
487      mtx_unlock(&sp->snd_mtx);
488 
489      return pq;
490 }
491 
492 static __inline void
i_rqueue_pdu(isc_session_t * sp,pduq_t * pq)493 i_rqueue_pdu(isc_session_t *sp, pduq_t *pq)
494 {
495      mtx_lock(&sp->snd_mtx);
496      KASSERT(pq->pduq != NULL, ("pq->pduq is NULL"));
497      TAILQ_INSERT_TAIL(pq->pduq, pq, pq_link);
498      mtx_unlock(&sp->snd_mtx);
499 }
500 
501 /*
502  | Waiting for ACK (or something :-)
503  */
504 static __inline void
i_nqueue_hld(isc_session_t * sp,pduq_t * pq)505 i_nqueue_hld(isc_session_t *sp, pduq_t *pq)
506 {
507      getbintime(&pq->ts);
508      mtx_lock(&sp->hld_mtx);
509      if(++sp->stats.nhld > sp->stats.max_hld)
510 	  sp->stats.max_hld = sp->stats.nhld;
511      TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link);
512      mtx_unlock(&sp->hld_mtx);
513      return;
514 }
515 
516 static __inline void
i_remove_hld(isc_session_t * sp,pduq_t * pq)517 i_remove_hld(isc_session_t *sp, pduq_t *pq)
518 {
519      mtx_lock(&sp->hld_mtx);
520      sp->stats.nhld--;
521      TAILQ_REMOVE(&sp->hld, pq, pq_link);
522      mtx_unlock(&sp->hld_mtx);
523 }
524 
525 static __inline pduq_t *
i_dqueue_hld(isc_session_t * sp)526 i_dqueue_hld(isc_session_t *sp)
527 {
528      pduq_t *pq;
529 
530      mtx_lock(&sp->hld_mtx);
531      if((pq = TAILQ_FIRST(&sp->hld)) != NULL) {
532 	  sp->stats.nhld--;
533 	  TAILQ_REMOVE(&sp->hld, pq, pq_link);
534      }
535      mtx_unlock(&sp->hld_mtx);
536 
537      return pq;
538 }
539 
540 static __inline pduq_t *
i_search_hld(isc_session_t * sp,int itt,int keep)541 i_search_hld(isc_session_t *sp, int itt, int keep)
542 {
543      pduq_t	*pq, *tmp;
544 
545      pq = NULL;
546 
547      mtx_lock(&sp->hld_mtx);
548      TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) {
549 	  if(pq->pdu.ipdu.bhs.itt == itt) {
550 	       if(!keep) {
551 		    sp->stats.nhld--;
552 		    TAILQ_REMOVE(&sp->hld, pq, pq_link);
553 	       }
554 	       break;
555 	  }
556      }
557      mtx_unlock(&sp->hld_mtx);
558 
559      return pq;
560 }
561 
562 static __inline void
i_acked_hld(isc_session_t * sp,pdu_t * op)563 i_acked_hld(isc_session_t *sp, pdu_t *op)
564 {
565      pduq_t	*pq, *tmp;
566      u_int exp = sp->sn.expCmd;
567 
568      pq = NULL;
569      mtx_lock(&sp->hld_mtx);
570      TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) {
571 	  if((op && op->ipdu.bhs.itt == pq->pdu.ipdu.bhs.itt)
572 	     || (pq->ccb == NULL
573 		 && (pq->pdu.ipdu.bhs.opcode != ISCSI_WRITE_DATA)
574 		 && SNA_GT(exp, ntohl(pq->pdu.ipdu.bhs.ExpStSN)))) {
575 	       sp->stats.nhld--;
576 	       TAILQ_REMOVE(&sp->hld, pq, pq_link);
577 	       pdu_free(sp->isc, pq);
578 	  }
579      }
580      mtx_unlock(&sp->hld_mtx);
581 }
582 
583 static __inline void
i_mbufcopy(struct mbuf * mp,caddr_t dp,int len)584 i_mbufcopy(struct mbuf *mp, caddr_t dp, int len)
585 {
586      struct mbuf *m;
587      caddr_t bp;
588 
589      for(m = mp; m != NULL; m = m->m_next) {
590 	  bp = mtod(m, caddr_t);
591 	  /*
592 	   | the pdu is word (4 octed) aligned
593 	   | so len <= packet
594 	   */
595 	  memcpy(dp, bp, MIN(len, m->m_len));
596 	  dp += m->m_len;
597 	  len -= m->m_len;
598 	  if(len <= 0)
599 	       break;
600      }
601 }
602