1.\" $NetBSD: mbuf.9,v 1.36 2005/06/02 11:38:28 bad Exp $ 2.\" 3.\" Copyright (c) 1997 The NetBSD Foundation, Inc. 4.\" All rights reserved. 5.\" 6.\" This documentation is derived from text contributed to The NetBSD Foundation 7.\" by S.P.Zeidler (aka stargazer). 8.\" 9.\" Redistribution and use in source and binary forms, with or without 10.\" modification, are permitted provided that the following conditions 11.\" are met: 12.\" 1. Redistributions of source code must retain the above copyright 13.\" notice, this list of conditions and the following disclaimer. 14.\" 2. Redistributions in binary form must reproduce the above copyright 15.\" notice, this list of conditions and the following disclaimer in the 16.\" documentation and/or other materials provided with the distribution. 17.\" 3. All advertising materials mentioning features or use of this software 18.\" must display the following acknowledgement: 19.\" This product includes software developed by the NetBSD 20.\" Foundation, Inc. and its contributors. 21.\" 4. Neither the name of The NetBSD Foundation nor the names of its 22.\" contributors may be used to endorse or promote products derived 23.\" from this software without specific prior written permission. 24.\" 25.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35.\" POSSIBILITY OF SUCH DAMAGE. 36.\" 37.Dd March 18, 2005 38.Dt MBUF 9 39.Os 40.Sh NAME 41.Nm mbuf , 42.Nm m_get , 43.Nm m_getclr , 44.Nm m_gethdr , 45.Nm m_devget , 46.Nm m_copym , 47.Nm m_copypacket , 48.Nm m_copydata , 49.Nm m_copyback , 50.Nm m_copyback_cow , 51.Nm m_cat , 52.Nm m_dup , 53.Nm m_makewritable , 54.Nm m_prepend , 55.Nm m_pulldown , 56.Nm m_pullup , 57.Nm m_split , 58.Nm m_adj , 59.Nm m_apply , 60.Nm m_free , 61.Nm m_freem , 62.Nm mtod , 63.Nm MGET , 64.Nm MGETHDR , 65.Nm MEXTMALLOC , 66.Nm MEXTADD , 67.Nm MCLGET , 68.Nm M_COPY_PKTHDR , 69.Nm M_ALIGN , 70.Nm MH_ALIGN , 71.Nm M_LEADINGSPACE , 72.Nm M_TRAILINGSPACE , 73.Nm M_PREPEND , 74.Nm MCHTYPE , 75.Nm MEXTREMOVE , 76.Nm MFREE , 77.Nd "functions and macros for managing memory used by networking code" 78.Sh SYNOPSIS 79.In sys/mbuf.h 80.Ft struct mbuf * 81.Fn m_get "int nowait" "int type" 82.Ft struct mbuf * 83.Fn m_getclr "int nowait" "int type" 84.Ft struct mbuf * 85.Fn m_gethdr "int nowait" "int type" 86.Ft struct mbuf * 87.Fn m_devget "char *buf" "int totlen" "int off0" "struct ifnet *ifp" "void (*copy)(const void *, void *, size_t)" 88.Ft struct mbuf * 89.Fn m_copym "struct mbuf *m" "int off0" "int len" "int wait" 90.Ft struct mbuf * 91.Fn m_copypacket "struct mbuf *m" "int how" 92.Ft void 93.Fn m_copydata "struct mbuf *m" "int off" "int len" "caddr_t cp" 94.Ft void 95.Fn m_copyback "struct mbuf *m0" "int off" "int len" "caddr_t cp" 96.Ft struct mbuf * 97.Fn m_copyback_cow "struct mbuf *m0" "int off" "int len" "caddr_t cp" "int how" 98.Ft int 99.Fn m_makewritable "struct mbuf **mp" "int off" "int len" "int how" 100.Ft void 101.Fn m_cat "struct mbuf *m" "struct mbuf *n" 102.Ft struct mbuf * 103.Fn m_dup "struct mbuf *m" "int off0" "int len" "int wait" 104.Ft struct mbuf * 105.Fn m_prepend "struct mbuf *m" "int len" "int how" 106.Ft struct mbuf * 107.Fn m_pulldown "struct mbuf *m" "int off" "int len" "int *offp" 108.Ft struct mbuf * 109.Fn m_pullup "struct mbuf *n" "int len" 110.Ft struct mbuf * 111.Fn m_split "struct mbuf *m0" "int len0" "int wait" 112.Ft void 113.Fn m_adj "struct mbuf *mp" "int req_len" 114.Ft int 115.Fn m_apply "struct mbuf *m" "int off" "int len" "int *f(void *, caddr_t, unsigned int)" "void *arg" 116.Ft struct mbuf * 117.Fn m_free "struct mbuf *m" 118.Ft void 119.Fn m_freem "struct mbuf *m" 120.Ft datatype 121.Fn mtod "struct mbuf *m" "datatype" 122.Ft void 123.Fn MGET "struct mbuf *m" "int how" "int type" 124.Ft void 125.Fn MGETHDR "struct mbuf *m" "int how" "int type" 126.Ft void 127.Fn MEXTMALLOC "struct mbuf *m" "int len" "int how" 128.Ft void 129.Fn MEXTADD "struct mbuf *m" "caddr_t buf" "int size" "int type" "void (*free)(struct mbuf *, caddr_t, size_t, void *)" "void *arg" 130.Ft void 131.Fn MCLGET "struct mbuf *m" "int how" 132.Ft void 133.Fn M_COPY_PKTHDR "struct mbuf *to" "struct mbuf *from" 134.Ft void 135.Fn M_ALIGN "struct mbuf *m" "int len" 136.Ft void 137.Fn MH_ALIGN "struct mbuf *m" "int len" 138.Ft int 139.Fn M_LEADINGSPACE "struct mbuf *m" 140.Ft int 141.Fn M_TRAILINGSPACE "struct mbuf *m" 142.Ft void 143.Fn M_PREPEND "struct mbuf *m" "int plen" "int how" 144.Ft void 145.Fn MCHTYPE "struct mbuf *m" "int type" 146.Ft void 147.Fn MEXTREMOVE "struct mbuf *m" 148.Ft void 149.Fn MFREE "struct mbuf *m" "struct mbuf *n" 150.Sh DESCRIPTION 151The 152.Nm 153functions and macros provide an easy and consistent way to handle 154a networking stack's memory management needs. 155An 156.Nm 157consists of a header and a data area. 158It is of a fixed size, 159.Dv MSIZE 160.Pq defined in Aq Pa machine/param.h , 161which includes overhead. 162The header contains a pointer to the next 163.Nm 164in the 165.Sq "mbuf chain" , 166a pointer to the next 167.Sq "mbuf chain" , 168a pointer to the data area, the amount of data in this mbuf, its type 169and a 170.Dv flags 171field. 172.Pp 173The 174.Dv type 175variable can signify: 176.Bl -tag -compact -offset indent -width "XXXXXXXXXXX" 177.It Dv MT_FREE 178the mbuf should be on the ``free'' list 179.It Dv MT_DATA 180data was dynamically allocated 181.It Dv MT_HEADER 182data is a packet header 183.It Dv MT_SONAME 184data is a socket name 185.It Dv MT_SOOPTS 186data is socket options 187.It Dv MT_FTABLE 188data is the fragment reassembly header 189.It Dv MT_CONTROL 190mbuf contains ancillary \&(protocol control\&) data 191.It Dv MT_OOBDATA 192mbuf contains out-of-band data. 193.El 194.Pp 195The 196.Dv flags 197variable contains information describing the 198.Nm , 199notably: 200.Bl -tag -compact -offset indent -width "XXXXXXXXXXX" 201.It Dv M_EXT 202has external storage 203.It Dv M_PKTHDR 204is start of record 205.It Dv M_EOR 206is end of record 207.It Dv M_CLUSTER 208external storage is a cluster. 209.El 210.Pp 211If an 212.Nm 213designates the start of a record 214.Pq Dv M_PKTHDR , 215its 216.Dv flags 217field may contain additional information describing the content of 218the record: 219.Bl -tag -compact -offset indent -width "XXXXXXXXXXX" 220.It Dv M_BCAST 221sent/received as link-level broadcast 222.It Dv M_MCAST 223sent/received as link-level multicast 224.It Dv M_LINK0 , 225.It Dv M_LINK1 , 226.It Dv M_LINK2 227three link-level specific flags. 228.El 229.Pp 230An 231.Nm 232may add a single 233.Sq "mbuf cluster" 234of 235.Dv MCLBYTES 236bytes 237.Pq also defined in Aq Pa machine/param.h , 238which has no additional overhead 239and is used instead of the internal data area; this is done when at least 240.Dv MINCLSIZE 241bytes of data must be stored. 242.Pp 243When the 244.Dv M_EXT 245flag is raised for an mbuf, 246the external storage area could be shared among multiple mbufs. 247Be careful when you attempt to overwrite the data content of the mbuf. 248.Bl -tag -width compact 249.It Fn m_get "int nowait" "int type" 250Allocates an mbuf and initializes it to contain internal data. 251The 252.Fa nowait 253parameter is a choice of 254.Dv M_WAIT / M_DONTWAIT 255from caller. 256.Dv M_WAIT 257means the call cannot fail, but may take forever. 258The 259.Fa type 260parameter is an mbuf type. 261.It Fn m_getclr "int nowait" "int type" 262Allocates an mbuf and initializes it to contain internal data, then 263zeros the data area. 264The 265.Fa nowait 266parameter is a choice of 267.Dv M_WAIT / M_DONTWAIT 268from caller. 269The 270.Fa type 271parameter is an mbuf type. 272.It Fn m_gethdr "int nowait" "int type" 273Allocates an mbuf and initializes it to contain a packet header and internal 274data. 275The 276.Fa nowait 277parameter is a choice of 278.Dv M_WAIT / M_DONTWAIT 279from caller. 280The 281.Fa type 282parameter is an mbuf type. 283.It Fn m_devget "char *buf" "int totlen" "int off0" "struct ifnet *ifp" "void (*copy)(const void *, void *, size_t)" 284Copies 285.Fa len 286bytes from device local memory into mbufs using copy routine 287.Fa copy . 288If parameter 289.Fa off 290is non-zero, the packet is supposed to be trailer-encapsulated and 291.Fa off 292bytes plus the type and length fields will be skipped before copying. 293Returns the top of the mbuf chain it created. 294.It Fn m_copym "struct mbuf *m" "int off0" "int len" "int wait" 295Creates a copy of an mbuf chain starting 296.Fa off0 297bytes from the beginning, continuing for 298.Fa len 299bytes. 300If the 301.Fa len 302requested is 303.Dv M_COPYALL , 304the complete mbuf chain will be copied. 305The 306.Fa wait 307parameter is a choice of 308.Dv M_WAIT / M_DONTWAIT 309from caller. 310.It Fn m_copypacket "struct mbuf *m" "int how" 311Copies an entire packet, including header (which must be present). 312This function is an optimization of the common case 313.Li m_copym(m, 0, Dv M_COPYALL, Fa how ) . 314.It Fn m_copydata "struct mbuf *m" "int off" "int len" "caddr_t cp" 315Copies 316.Fa len 317bytes data from mbuf chain 318.Fa m 319into the buffer 320.Fa cp , 321starting 322.Fa off 323bytes from the beginning. 324.It Fn m_copyback "struct mbuf *m0" "int off" "int len" "caddr_t cp" 325Copies 326.Fa len 327bytes data from buffer 328.Fa cp 329back into the mbuf chain 330.Fa m0 , 331starting 332.Fa off 333bytes from the beginning of the chain, extending the mbuf chain if necessary. 334.Fn m_copyback 335can only fail when extending the chain. 336The caller should check for this kind of failure 337by checking the resulting length of the chain in that case. 338It is an error to use 339.Fn m_copyback 340on read-only mbufs. 341.It Fn m_copyback_cow "struct mbuf *m0" "int off" "int len" "caddr_t cp" \ 342"int how" 343Copies 344.Fa len 345bytes data from buffer 346.Fa cp 347back into the mbuf chain 348.Fa m0 349as 350.Fn m_copyback 351does. 352Unlike 353.Fn m_copyback , 354it is safe to use 355.Fn m_copyback_cow 356on read-only mbufs. 357If needed, 358.Fn m_copyback_cow 359automatically allocates new mbufs and adjusts the chain. 360It returns a pointer to the resulting mbuf chain on success. 361Otherwise, it returns 362.Dv NULL . 363In that case, the original mbuf 364.Fa m0 365will be freed. 366The 367.Fa how 368parameter is a choice of 369.Dv M_WAIT / M_DONTWAIT 370from the caller. 371Unlike 372.Fn m_copyback , 373extending the mbuf chain isn't supported. 374It is an error to attempt to extend the mbuf chain using 375.Fn m_copyback_cow . 376.It Fn m_makewritable "struct mbuf **mp" "int off" "int len" "int how" 377Rearranges an mbuf chain so that 378.Fa len 379bytes from offset 380.Fa off 381are writable. 382When it meets read-only mbufs, it allocates new mbufs, adjusts the chain as 383.Fn m_copyback_cow 384does, and copies the original content into them. 385The 386.Fa how 387parameter is a choice of 388.Dv M_WAIT / M_DONTWAIT 389from the caller. 390.Fn m_makewritable 391preserves the contents of the mbuf chain even in the case of failure. 392It updates a pointer to the mbuf chain pointed to by 393.Fa mp . 394It returns 0 on success. 395Otherwise, it returns an error code, typically 396.Er ENOBUFS . 397.It Fn m_cat "struct mbuf *m" "struct mbuf *n" 398Concatenates mbuf chain 399.Fa n 400to 401.Fa m . 402Both chains must be of the same type; packet headers will 403.Em not 404be updated if present. 405.It Fn m_dup "struct mbuf *m" "int off0" "int len" "int wait" 406Similarly to 407.Fn m_copym , 408the function creates a copy of an mbuf chain starting 409.Fa off0 410bytes from the beginning, continuing for 411.Fa len 412bytes. 413While 414.Fn m_copym 415tries to share external storage for mbufs with 416.Dv M_EXT 417flag, 418.Fn m_dup 419will deep-copy the whole data content into new mbuf chain 420and avoids shared external storage. 421.It Fn m_prepend "struct mbuf *m" "int len" "int how" 422Lesser-used path for 423.Fn M_PREPEND : 424allocates new mbuf 425.Fa m 426of size 427.Fa len 428to prepend to the chain, copying junk along. 429The 430.Fa how 431parameter is a choice of 432.Dv M_WAIT / M_DONTWAIT 433from caller. 434.It Fn m_pulldown "struct mbuf *m" "int off" "int len" "int *offp" 435Rearranges an mbuf chain so that 436.Fa len 437bytes from offset 438.Fa off 439are contiguous and in the data area of an mbuf. 440The return value points to an mbuf in the middle of the mbuf chain 441.Fa m . 442If we call the return value 443.Fa n , 444the contiguous data region is available at 445.Li "mtod(n, caddr_t) + *offp" , 446or 447.Li "mtod(n, caddr_t)" 448if 449.Fa offp 450is 451.Dv NULL . 452The top of the mbuf chain 453.Fa m , 454and mbufs up to 455.Fa off , 456will not be modified. 457On successful return, it is guaranteed that the mbuf pointed to by 458.Fa n 459does not have a shared external storage, 460therefore it is safe to update the contiguous region. 461Returns 462.Dv NULL 463and frees the mbuf chain on failure. 464.Fa len 465must be smaller or equal than 466.Dv MCLBYTES . 467.It Fn m_pullup "struct mbuf *m" "int len" 468Rearranges an mbuf chain so that 469.Fa len 470bytes are contiguous 471and in the data area of an mbuf (so that 472.Fn mtod 473will work for a structure of size 474.Fa len ) . 475Returns the resulting 476mbuf chain on success, frees it and returns 477.Dv NULL 478on failure. 479If there is room, it will add up to 480.Dv max_protohdr 481- 482.Fa len 483extra bytes to the 484contiguous region to possibly avoid being called again. 485.Fa len 486must be smaller or equal than 487.Dv MHLEN . 488.It Fn m_split "struct mbuf *m0" "int len0" "int wait" 489Partitions an mbuf chain in two pieces, returning the tail, 490which is all but the first 491.Fa len0 492bytes. 493In case of failure, it returns 494.Dv NULL 495and attempts to 496restore the chain to its original state. 497.It Fn m_adj "struct mbuf *mp" "int req_len" 498Shaves off 499.Fa req_len 500bytes from head or tail of the (valid) data area. 501If 502.Fa req_len 503is greater than zero, front bytes are being shaved off, if it's smaller, 504from the back (and if it is zero, the mbuf will stay bearded). 505This function does not move data in any way, but is used to manipulate the 506data area pointer and data length variable of the mbuf in a non-clobbering 507way. 508.It Fn m_apply "struct mbuf *m" "int off" "int len" "int (*f)(void *, caddr_t, unsigned int)" "void *arg" 509Apply function 510.Fa f 511to the data in an mbuf chain starting 512.Fa off 513bytes from the beginning, continuing for 514.Fa len 515bytes. 516Neither 517.Fa off 518nor 519.Fa len 520may be negative. 521.Fa arg 522will be supplied as first argument for 523.Fa f , 524the second argument will be the pointer to the data buffer of a 525packet (starting after 526.Fa off 527bytes in the stream), and the third argument is the amount 528of data in bytes in this call. 529If 530.Fa f 531returns something not equal to zero 532.Fn m_apply 533will bail out, returning the return code of 534.Fa f . 535Upon successful completion it will return zero. 536.It Fn m_free "struct mbuf *m" 537Frees mbuf 538.Fa m . 539.It Fn m_freem "struct mbuf *m" 540Frees the mbuf chain beginning with 541.Fa m . 542This function contains the elementary sanity check for a 543.Dv NULL 544pointer. 545.It Fn mtod "struct mbuf *m" "datatype" 546Returns a pointer to the data contained in the specified mbuf 547.Fa m , 548type-casted to the specified data type 549.Fa datatype . 550Implemented as a macro. 551.It Fn MGET "struct mbuf *m" "int how" "int type" 552Allocates mbuf 553.Fa m 554and initializes it to contain internal data. 555See 556.Fn m_get . 557Implemented as a macro. 558.It Fn MGETHDR "struct mbuf *m" "int how" "int type" 559Allocates mbuf 560.Fa m 561and initializes it to contain a packet header. 562See 563.Fn m_gethdr . 564Implemented as a macro. 565.It Fn MEXTMALLOC "struct mbuf *m" "int len" "int how" 566Allocates external storage of size 567.Fa len 568for mbuf 569.Fa m . 570The 571.Fa how 572parameter is a choice of 573.Dv M_WAIT / M_DONTWAIT 574from caller. 575The flag 576.Dv M_EXT 577is set upon success. 578Implemented as a macro. 579.It Fn MEXTADD "struct mbuf *m" "caddr_t buf" "int size" "int type" "void (*free)(struct mbuf *, caddr_t, size_t, void *)" "void *arg" 580Adds pre-allocated external storage 581.Fa buf 582to a normal mbuf 583.Fa m ; 584the parameters 585.Fa size , 586.Fa type , 587.Fa free 588and 589.Fa arg 590describe the external storage. 591.Fa size 592is the size of the storage, 593.Fa type 594describes its 595.Xr malloc 9 596type, 597.Fa free 598is a free routine (if not the usual one), and 599.Fa arg 600is a possible argument to the free routine. 601The flag 602.Dv M_EXT 603is set upon success. 604Implemented as a macro. 605If a free routine is specified, it will be called when the mbuf is freed or 606when MEXTREMOVE is called for the mbuf. 607In the case of former, the first argument for a free routine is the mbuf 608.Fa m 609and the routine is expected to free it in addition to the external storage 610pointed by second argument. 611In the case of latter, the first argument for the routine is NULL. 612.It Fn MCLGET "struct mbuf *m" "int how" 613Allocates and adds an mbuf cluster to a normal mbuf 614.Fa m . 615The 616.Fa how 617parameter is a choice of 618.Dv M_WAIT / M_DONTWAIT 619from caller. 620The flag 621.Dv M_EXT 622is set upon success. 623Implemented as a macro. 624.It Fn M_COPY_PKTHDR "struct mbuf *to" "struct mbuf *from" 625Copies the mbuf pkthdr from mbuf 626.Fa from 627to mbuf 628.Fa to . 629.Fa from 630must have the type flag 631.Dv M_PKTHDR 632set, and 633.Fa to 634must be empty. 635Implemented as a macro. 636.It Fn M_ALIGN "struct mbuf *m" "int len" 637Sets the data pointer of a newly allocated mbuf 638.Fa m 639to 640.Fa len 641bytes from the end of the mbuf data area, so that 642.Fa len 643bytes of data written to the mbuf 644.Fa m , 645starting at the data pointer, will be aligned to the end of the data area. 646Implemented as a macro. 647.It Fn MH_ALIGN "struct mbuf *m" "int len" 648Sets the data pointer of a newly allocated packetheader mbuf 649.Fa m 650to 651.Fa len 652bytes from the end of the mbuf data area, so that 653.Fa len 654bytes of data written to the mbuf 655.Fa m , 656starting at the data pointer, will be aligned to the end of the data area. 657Implemented as a macro. 658.It Fn M_LEADINGSPACE "struct mbuf *m" 659Returns the amount of space available before the current start of valid 660data in mbuf 661.Fa m . 662Returns 0 if the mbuf data part is shared across multiple mbufs 663.Pq i.e. not writable . 664Implemented as a macro. 665.It Fn M_TRAILINGSPACE "struct mbuf *m" 666Returns the amount of space available after the current end of valid 667data in mbuf 668.Fa m . 669Returns 0 if the mbuf data part is shared across multiple mbufs 670.Pq i.e. not writable . 671Implemented as a macro. 672.It Fn M_PREPEND "struct mbuf *m" "int plen" "int how" 673Prepends space of size 674.Fa plen 675to mbuf 676.Fa m . 677If a new mbuf must be allocated, 678.Fa how 679specifies whether to wait. 680If 681.Fa how 682is 683.Dv M_DONTWAIT 684and allocation fails, the original mbuf chain is freed and 685.Fa m 686is set to 687.Dv NULL . 688Implemented as a macro. 689.It Fn MCHTYPE "struct mbuf *m" "int type" 690Change mbuf 691.Fa m 692to new type 693.Fa type . 694Implemented as a macro. 695.It Fn MEXTREMOVE "struct mbuf *m" 696Removes external storage from mbuf 697.Fa m . 698The flag 699.Dv M_EXT 700is removed. 701Implemented as a macro. 702.It Fn MFREE "struct mbuf *m" "struct mbuf *n" 703Frees a single mbuf 704.Fa m 705and places the successor, if any, in mbuf 706.Fa n . 707Implemented as a macro. 708.El 709.Sh FILES 710The 711.Nm 712management functions are implemented within the file 713.Pa sys/kern/uipc_mbuf.c . 714Function prototypes, and the functions implemented as macros 715are located in 716.Pa sys/sys/mbuf.h . 717Both pathnames are relative to the root of the 718.Nx 719source tree, 720.Pa /usr/src . 721.Sh SEE ALSO 722.Pa /usr/share/doc/smm/18.net , 723.Xr netstat 1 , 724.Xr malloc 9 725.Rs 726.%A Jun-ichiro Hagino 727.%T "Mbuf issues in 4.4BSD IPv6/IPsec support (experiences from KAME IPv6/IPsec implementation)" 728.%B "Proceedings of the freenix track: 2000 USENIX annual technical conference" 729.%D June 2000 730.Re 731.Sh AUTHORS 732The original mbuf data structures were designed by Rob Gurwitz 733when he did the initial TCP/IP implementation at BBN. 734.br 735Further extensions and enhancements were made by Bill Joy, Sam Leffler, 736and Mike Karels at CSRG. 737.br 738Current implementation of external storage by 739.An Matt Thomas 740.Aq matt@3am-software.com 741and 742.An Jason R. Thorpe 743.Aq thorpej@NetBSD.org . 744