1/* 2 * Copyright (c) 2008 - 2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <sys/types.h> 25#include <ctype.h> 26#include <errno.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31#include <netsmb/upi_mbuf.h> 32#include <netsmb/smb_lib.h> 33 34 35/* mbuf flags */ 36#define M_EXT 0x0001 /* has associated external storage */ 37#define M_PKTHDR 0x0002 /* start of record */ 38#define M_EOR 0x0004 /* end of record */ 39 40 41struct smb_mbuf { 42 uint32_t m_type; 43 uint32_t m_flags; 44 size_t m_maxlen; 45 size_t m_len; 46 size_t m_pkthdr_len; 47 char *m_data; 48 struct smb_mbuf *m_next; 49 void (*m_extfree)(caddr_t , size_t, caddr_t); 50 caddr_t m_extarg; 51}; 52 53/* 54 * mbuf_free 55 * 56 * Frees a single mbuf. Not commonly used outside of the file because it 57 * doesn't touch the rest of the mbufs on the chain. 58 * params: 59 * mbuf - The mbuf to free. 60 * result: 61 * The next mbuf in the chain. 62 */ 63mbuf_t mbuf_free(mbuf_t mbuf) 64{ 65 mbuf_t next = NULL; 66 67 if (mbuf == NULL) 68 return next; /* nothing to free */ 69 70 next = mbuf->m_next; 71 if (mbuf->m_type == MBUF_TYPE_FREE) { 72 smb_log_info("%s: Double FREE", ASL_LEVEL_DEBUG, __FUNCTION__); 73 } 74 if (mbuf->m_flags & M_EXT) { 75 if (mbuf->m_extfree) 76 mbuf->m_extfree(mbuf->m_extarg, mbuf->m_maxlen, (caddr_t)mbuf->m_data); 77 } else if (mbuf->m_data) { 78 free(mbuf->m_data); 79 } 80 mbuf->m_next = NULL; 81 mbuf->m_type = MBUF_TYPE_FREE; 82 mbuf->m_data = NULL; 83 free(mbuf); 84 return next; 85} 86 87/* 88 * smb_mbuf_get 89 * 90 * Internal routine that allocates all userland mbufs. Only creates 91 * data storage if maxlen is not zero. 92 * params: 93 * how - How to create the mbuf, always MBUF_WAITOK in userland. 94 * type - Type of mbuf to create, always MBUF_TYPE_DATA in userland. 95 * mbuf - Return location for the mbuf we created. 96 * maxlen - The data size that should be allocated for this mbuf 97 * result: 98 * Error - Either zero or the appropriate errno 99 */ 100static 101int smb_mbuf_get(uint32_t how, uint32_t type, mbuf_t *mbuf, size_t maxlen) 102{ 103 struct smb_mbuf *m; 104 105 if ((type != MBUF_TYPE_DATA) || (how != MBUF_WAITOK)) 106 return (EINVAL); 107 108 m = malloc(sizeof(struct smb_mbuf)); 109 if (m == NULL) 110 return ENOMEM; 111 112 bzero(m, sizeof(struct smb_mbuf)); 113 m->m_type = type; 114 if (maxlen) { 115 m->m_data = malloc(maxlen); 116 if (m->m_data == NULL) { 117 (void)mbuf_free(m); 118 return ENOMEM; 119 } 120 m->m_maxlen = maxlen; 121 } 122 *mbuf = m; 123 return 0; 124} 125 126/* 127 * mbuf_freem 128 * 129 * Frees a chain of mbufs link through mnext. 130 * params: 131 * mbuf - The first mbuf in the chain to free. 132 */ 133void mbuf_freem(mbuf_t mbuf) 134{ 135 struct smb_mbuf *m; 136 137 while (mbuf) { 138 m = mbuf_free(mbuf); 139 mbuf = m; 140 } 141} 142 143/* 144 * mbuf_gethdr 145 * 146 * Allocates an mbuf without a cluster for external data. Sets a flag to 147 * indicate there is a packet header and initializes the packet header. 148 * params: 149 * how - How to create the mbuf, always MBUF_WAITOK in userland. 150 * type - Type of mbuf to create, always MBUF_TYPE_DATA in userland. 151 * mbuf - Return location for the mbuf we created. 152 * result: 153 * Error - Either zero or the appropriate errno 154 */ 155int mbuf_gethdr(uint32_t how, uint32_t type, mbuf_t *mbuf) 156{ 157 int error = smb_mbuf_get(how, type, mbuf, getpagesize()); 158 if (error) 159 return error; 160 161 (*mbuf)->m_flags |= M_PKTHDR | M_EOR; 162 return 0; 163} 164 165/* 166 * mbuf_get 167 * 168 * Allocates an mbuf without a cluster for external data. 169 * params: 170 * how - How to create the mbuf, always MBUF_WAITOK in userland. 171 * type - Type of mbuf to create, always MBUF_TYPE_DATA in userland. 172 * mbuf - Return location for the mbuf we created. 173 * result: 174 * Error - Either zero or the appropriate errno 175 */ 176int mbuf_get(uint32_t how, uint32_t type, mbuf_t *mbuf) 177{ 178 return smb_mbuf_get(how, type, mbuf, getpagesize()); 179} 180 181/* 182 * mbuf_getcluster 183 * 184 * Allocate a cluster of the requested size and attach it to an mbuf for use as 185 * external data. If mbuf points to a NULL mbuf_t, an mbuf will be allocated for 186 * you. If mbuf points to a non-NULL mbuf_t, mbuf_getcluster may return a different 187 * mbuf_t than the one you passed in. 188 * params: 189 * how - How to create the mbuf, always MBUF_WAITOK in userland. 190 * type - Type of mbuf to create, always MBUF_TYPE_DATA in userland. 191 * size - The size of the cluster to be allocated. This routine allows 192 * any size, unlike the kernel kpi mbuf code. 193 * mbuf - The mbuf the cluster will be attached to. 194 * result: 195 * Error - Either zero or the appropriate errno 196 */ 197int mbuf_getcluster(uint32_t how, uint32_t type, size_t size, mbuf_t *mbuf) 198{ 199 int error; 200 /* We currently relocate the mbuf always */ 201 if (*mbuf) { 202 mbuf_freem( *mbuf); 203 *mbuf = NULL; 204 } 205 error = smb_mbuf_get(how, type, mbuf, size); 206 if (!error && *mbuf) 207 (*mbuf)->m_flags |= M_PKTHDR | M_EOR; 208 return error; 209} 210 211/* 212 * mbuf_attachcluster 213 * 214 * Attach an external buffer as a cluster for an mbuf. If mbuf points to a NULL 215 * mbuf_t, an mbuf will be allocated for you. If mbuf points to a non-NULL mbuf_t, 216 * the user-supplied mbuf will be used instead. 217 * params: 218 * how - How to create the mbuf, always MBUF_WAITOK in userland. 219 * type - Type of mbuf to create, always MBUF_TYPE_DATA in userland. 220 * mbuf - Pointer to the address of the mbuf; if NULL, an mbuf will be 221 * allocated, otherwise, it must point to a valid mbuf address. 222 * extbuf - Address of the external buffer. 223 * extfree - Free routine for the external buffer; the caller is required 224 * to defined a routine that will be invoked when the mbuf is 225 * freed. Userland code allows this to be null. 226 * extsize - Size of the external buffer. 227 * extarg - Private value that will be passed to the free routine when it 228 * is called at the time the mbuf is freed. 229 * result: 230 * Error - Either zero or the appropriate errno 231 */ 232int mbuf_attachcluster(uint32_t how, uint32_t type, 233 mbuf_t *mbuf, void * extbuf, 234 void (*extfree)(caddr_t , size_t, caddr_t), 235 size_t extsize, caddr_t extarg) 236{ 237 int error = 0; 238 239 if ((extbuf == NULL) || (extsize == 0)) { 240 error = EINVAL; 241 } else if (*mbuf == NULL) { 242 error = smb_mbuf_get(how, type, mbuf, 0); 243 } else if ((*mbuf)->m_data) { 244 free((*mbuf)->m_data); 245 (*mbuf)->m_data = NULL; 246 } 247 if (error) 248 return error; 249 250 (*mbuf)->m_flags = M_EXT | M_PKTHDR | M_EOR; 251 (*mbuf)->m_maxlen = extsize; 252 (*mbuf)->m_data = extbuf; 253 (*mbuf)->m_extfree = extfree; 254 (*mbuf)->m_extarg = extarg; 255 return 0; 256} 257 258/* 259 * mbuf_len 260 * 261 * Gets the length of data in this mbuf. 262 * params: 263 * mbuf - The mbuf. 264 * result: 265 * size_t - The length of data in this mbuf. 266 */ 267size_t mbuf_len(const mbuf_t mbuf) 268{ 269 if (mbuf) { 270 return mbuf->m_len; 271 } else { 272 return 0; 273 } 274} 275 276/* 277 * mbuf_maxlen 278 * 279 * Retrieves the maximum length of data that may be stored in this mbuf. 280 * params: 281 * mbuf - The mbuf. 282 * result: 283 * size_t - The maximum length of data for this mbuf. 284 */ 285size_t mbuf_maxlen(const mbuf_t mbuf) 286{ 287 if (mbuf) { 288 return mbuf->m_maxlen; 289 } else { 290 return 0; 291 } 292} 293 294/* 295 * mbuf_setlen 296 * 297 * Sets the length of data in this packet. Be careful to not set the length over 298 * the space available in the mbuf. 299 * param: 300 * mbuf - The mbuf. 301 * len - The new length. 302 */ 303void mbuf_setlen(mbuf_t mbuf, size_t len) 304{ 305 if (mbuf) { 306 mbuf->m_len = len; 307 } 308} 309 310/* 311 * mbuf_pkthdr_len 312 * 313 * Returns the length as reported by the packet header. 314 * params: 315 * mbuf - The mbuf containing the packet header with the length to 316 * be changed. 317 * result: 318 * size_t - The length, in bytes, of the packet. 319 */ 320size_t mbuf_pkthdr_len(const mbuf_t mbuf) 321{ 322 return mbuf->m_pkthdr_len; 323} 324 325/* 326 * mbuf_pkthdr_setlen 327 * 328 * Sets the length of the packet in the packet header. 329 * params: 330 * mbuf - The mbuf containing the packet header. 331 */ 332void mbuf_pkthdr_setlen(mbuf_t mbuf, size_t len) 333{ 334 mbuf->m_pkthdr_len = len; 335} 336 337/* 338 * mbuf_pkthdr_adjustlen 339 * 340 * Adjusts the length of the packet in the packet header. 341 * params: 342 * mbuf - The mbuf containing the packet header. 343 * amount - The number of bytes to adjust the packet header length field. 344 */ 345void mbuf_pkthdr_adjustlen(mbuf_t mbuf, int amount) 346{ 347 mbuf->m_pkthdr_len += amount; 348} 349 350/* 351 * mbuf_next 352 * 353 * Returns the next mbuf in the chain. 354 * params: 355 * mbuf - The mbuf 356 * result: 357 * mbuf_t - The next mbuf in the chain. 358 */ 359mbuf_t mbuf_next(const mbuf_t mbuf) 360{ 361 if (mbuf) { 362 return mbuf->m_next; 363 } else { 364 return NULL; 365 } 366} 367 368/* 369 * mbuf_setnext 370 * 371 * Sets the next mbuf in the chain. 372 * params: 373 * mbuf - The mbuf 374 * next - The new next mbuf. 375 * result: 376 * Error - Either zero or the appropriate errno 377 */ 378int mbuf_setnext(mbuf_t mbuf, mbuf_t next) 379{ 380 if (!next || (next->m_type == MBUF_TYPE_FREE)) 381 return EINVAL; 382 if (mbuf->m_flags & M_EOR) { 383 mbuf->m_flags &= ~M_EOR; 384 next->m_flags |=M_EOR; 385 } 386 mbuf->m_next = next; 387 return 0; 388} 389 390/* 391 * mbuf_data 392 * 393 * Returns a pointer to the start of data in this mbuf. There may be additional 394 * data on chained mbufs. The data you're looking for may not be virtually 395 * contiguous if it spans more than one mbuf. In addition, data that is virtually 396 * contiguous might not be represented by physically contiguous pages; see 397 * further comments in mbuf_data_to_physical. Use mbuf_len to determine the length 398 * of data available in this mbuf. If a data structure you want to access stradles 399 * two mbufs in a chain, either use mbuf_pullup to get the data contiguous in one mbuf 400 * or copy the pieces of data from each mbuf in to a contiguous buffer. Using 401 * mbuf_pullup has the advantage of not having to copy the data. On the other hand, 402 * if you don't make sure there is space in the mbuf, mbuf_pullup may fail and 403 * free the mbuf. 404 * params: 405 * mbuf - The mbuf 406 * next - The new next mbuf. 407 * result: 408 * void * - A pointer to the data in the mbuf. 409 */ 410void *mbuf_data(const mbuf_t mbuf) 411{ 412 if (mbuf) { 413 return (void *)mbuf->m_data; 414 } else { 415 return NULL; 416 } 417} 418 419/* 420 * mbuf_trailingspace 421 * 422 * Determines the space available in the mbuf following the current data. 423 * params: 424 * mbuf - The mbuf 425 * result: 426 * size_t - The number of unused bytes following the current data. 427 */ 428size_t mbuf_trailingspace(const mbuf_t mbuf) 429{ 430 return mbuf->m_maxlen - mbuf->m_len; 431} 432 433/* 434 * mbuf_copydata 435 * 436 * Copies data out of an mbuf in to a specified buffer. If the data is stored in 437 * a chain of mbufs, the data will be copied from each mbuf in the chain until 438 * length bytes have been copied. 439 * params: 440 * mbuf - The mbuf chain to copy data out of. 441 * offset - The offset in to the mbuf to start copying. 442 * length - The number of bytes to copy. 443 * out_data - A pointer to the location where the data will be copied. 444 * result: 445 * Error - Either zero or the appropriate errno 446 */ 447int mbuf_copydata(const mbuf_t mbuf, size_t offset, size_t length, void *out_data) 448{ 449 /* Copied m_copydata, added error handling (don't just panic) */ 450 size_t count; 451 mbuf_t m = mbuf; 452 453 while (offset > 0) { 454 if (m == 0) 455 return EINVAL; 456 if (offset < (size_t)m->m_len) 457 break; 458 offset -= m->m_len; 459 m = m->m_next; 460 } 461 while (length > 0) { 462 if (m == 0) 463 return EINVAL; 464 count = m->m_len - offset > length ? length : m->m_len - offset; 465 bcopy((caddr_t)mbuf_data(m) + offset, out_data, count); 466 length -= count; 467 out_data = ((char*)out_data) + count; 468 offset = 0; 469 m = m->m_next; 470 } 471 472 return 0; 473} 474