Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_message.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2008 Martin Willi
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #define _GNU_SOURCE
! 17: #include <string.h>
! 18: #include <arpa/inet.h>
! 19:
! 20: #include "ha_message.h"
! 21:
! 22: #include <daemon.h>
! 23:
! 24: #define ALLOCATION_BLOCK 64
! 25:
! 26: typedef struct private_ha_message_t private_ha_message_t;
! 27:
! 28: /**
! 29: * Private data of an ha_message_t object.
! 30: */
! 31: struct private_ha_message_t {
! 32:
! 33: /**
! 34: * Public ha_message_t interface.
! 35: */
! 36: ha_message_t public;
! 37:
! 38: /**
! 39: * Allocated size of buf
! 40: */
! 41: size_t allocated;
! 42:
! 43: /**
! 44: * Buffer containing encoded data
! 45: */
! 46: chunk_t buf;
! 47: };
! 48:
! 49: ENUM(ha_message_type_names, HA_IKE_ADD, HA_IKE_IV,
! 50: "IKE_ADD",
! 51: "IKE_UPDATE",
! 52: "IKE_MID_INITIATOR",
! 53: "IKE_MID_RESPONDER",
! 54: "IKE_DELETE",
! 55: "CHILD_ADD",
! 56: "CHILD_DELETE",
! 57: "SEGMENT_DROP",
! 58: "SEGMENT_TAKE",
! 59: "STATUS",
! 60: "RESYNC",
! 61: "IKE_IV",
! 62: );
! 63:
! 64: typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
! 65:
! 66: /**
! 67: * Encoding if an ike_sa_id_t
! 68: */
! 69: struct ike_sa_id_encoding_t {
! 70: uint8_t ike_version;
! 71: uint64_t initiator_spi;
! 72: uint64_t responder_spi;
! 73: uint8_t initiator;
! 74: } __attribute__((packed));
! 75:
! 76: typedef struct identification_encoding_t identification_encoding_t;
! 77:
! 78: /**
! 79: * Encoding of a identification_t
! 80: */
! 81: struct identification_encoding_t {
! 82: uint8_t type;
! 83: uint8_t len;
! 84: char encoding[];
! 85: } __attribute__((packed));
! 86:
! 87: typedef struct host_encoding_t host_encoding_t;
! 88:
! 89: /**
! 90: * encoding of a host_t
! 91: */
! 92: struct host_encoding_t {
! 93: uint16_t port;
! 94: uint8_t family;
! 95: char encoding[];
! 96: } __attribute__((packed));
! 97:
! 98: typedef struct ts_encoding_t ts_encoding_t;
! 99:
! 100: /**
! 101: * encoding of a traffic_selector_t
! 102: */
! 103: struct ts_encoding_t {
! 104: uint8_t type;
! 105: uint8_t protocol;
! 106: uint16_t from_port;
! 107: uint16_t to_port;
! 108: uint8_t dynamic;
! 109: char encoding[];
! 110: } __attribute__((packed));
! 111:
! 112: METHOD(ha_message_t, get_type, ha_message_type_t,
! 113: private_ha_message_t *this)
! 114: {
! 115: return this->buf.ptr[1];
! 116: }
! 117:
! 118: /**
! 119: * check for space in buffer, increase if necessary
! 120: */
! 121: static void check_buf(private_ha_message_t *this, size_t len)
! 122: {
! 123: int increased = 0;
! 124:
! 125: while (this->buf.len + len > this->allocated)
! 126: { /* double size */
! 127: this->allocated += ALLOCATION_BLOCK;
! 128: increased++;
! 129: }
! 130: if (increased)
! 131: {
! 132: this->buf.ptr = realloc(this->buf.ptr, this->allocated);
! 133: }
! 134: }
! 135:
! 136: METHOD(ha_message_t, add_attribute, void,
! 137: private_ha_message_t *this, ha_message_attribute_t attribute, ...)
! 138: {
! 139: size_t len;
! 140: va_list args;
! 141:
! 142: check_buf(this, sizeof(uint8_t));
! 143: this->buf.ptr[this->buf.len] = attribute;
! 144: this->buf.len += sizeof(uint8_t);
! 145:
! 146: va_start(args, attribute);
! 147: switch (attribute)
! 148: {
! 149: /* ike_sa_id_t* */
! 150: case HA_IKE_ID:
! 151: case HA_IKE_REKEY_ID:
! 152: {
! 153: ike_sa_id_encoding_t *enc;
! 154: ike_sa_id_t *id;
! 155:
! 156: id = va_arg(args, ike_sa_id_t*);
! 157: check_buf(this, sizeof(ike_sa_id_encoding_t));
! 158: enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
! 159: this->buf.len += sizeof(ike_sa_id_encoding_t);
! 160: enc->initiator = id->is_initiator(id);
! 161: enc->ike_version = id->get_ike_version(id);
! 162: enc->initiator_spi = id->get_initiator_spi(id);
! 163: enc->responder_spi = id->get_responder_spi(id);
! 164: break;
! 165: }
! 166: /* identification_t* */
! 167: case HA_LOCAL_ID:
! 168: case HA_REMOTE_ID:
! 169: case HA_REMOTE_EAP_ID:
! 170: {
! 171: identification_encoding_t *enc;
! 172: identification_t *id;
! 173: chunk_t data;
! 174:
! 175: id = va_arg(args, identification_t*);
! 176: data = id->get_encoding(id);
! 177: check_buf(this, sizeof(identification_encoding_t) + data.len);
! 178: enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
! 179: this->buf.len += sizeof(identification_encoding_t) + data.len;
! 180: enc->type = id->get_type(id);
! 181: enc->len = data.len;
! 182: memcpy(enc->encoding, data.ptr, data.len);
! 183: break;
! 184: }
! 185: /* host_t* */
! 186: case HA_LOCAL_ADDR:
! 187: case HA_REMOTE_ADDR:
! 188: case HA_LOCAL_VIP:
! 189: case HA_REMOTE_VIP:
! 190: case HA_PEER_ADDR:
! 191: {
! 192: host_encoding_t *enc;
! 193: host_t *host;
! 194: chunk_t data;
! 195:
! 196: host = va_arg(args, host_t*);
! 197: data = host->get_address(host);
! 198: check_buf(this, sizeof(host_encoding_t) + data.len);
! 199: enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
! 200: this->buf.len += sizeof(host_encoding_t) + data.len;
! 201: enc->family = host->get_family(host);
! 202: enc->port = htons(host->get_port(host));
! 203: memcpy(enc->encoding, data.ptr, data.len);
! 204: break;
! 205: }
! 206: /* char* */
! 207: case HA_CONFIG_NAME:
! 208: {
! 209: char *str;
! 210:
! 211: str = va_arg(args, char*);
! 212: len = strlen(str) + 1;
! 213: check_buf(this, len);
! 214: memcpy(this->buf.ptr + this->buf.len, str, len);
! 215: this->buf.len += len;
! 216: break;
! 217: }
! 218: /* uint8_t */
! 219: case HA_IKE_VERSION:
! 220: case HA_INITIATOR:
! 221: case HA_IPSEC_MODE:
! 222: case HA_IPCOMP:
! 223: {
! 224: uint8_t val;
! 225:
! 226: val = va_arg(args, u_int);
! 227: check_buf(this, sizeof(val));
! 228: this->buf.ptr[this->buf.len] = val;
! 229: this->buf.len += sizeof(val);
! 230: break;
! 231: }
! 232: /* uint16_t */
! 233: case HA_ALG_DH:
! 234: case HA_ALG_PRF:
! 235: case HA_ALG_OLD_PRF:
! 236: case HA_ALG_ENCR:
! 237: case HA_ALG_ENCR_LEN:
! 238: case HA_ALG_INTEG:
! 239: case HA_INBOUND_CPI:
! 240: case HA_OUTBOUND_CPI:
! 241: case HA_SEGMENT:
! 242: case HA_ESN:
! 243: case HA_AUTH_METHOD:
! 244: {
! 245: uint16_t val;
! 246:
! 247: val = va_arg(args, u_int);
! 248: check_buf(this, sizeof(val));
! 249: *(uint16_t*)(this->buf.ptr + this->buf.len) = htons(val);
! 250: this->buf.len += sizeof(val);
! 251: break;
! 252: }
! 253: /** uint32_t */
! 254: case HA_CONDITIONS:
! 255: case HA_EXTENSIONS:
! 256: case HA_INBOUND_SPI:
! 257: case HA_OUTBOUND_SPI:
! 258: case HA_MID:
! 259: {
! 260: uint32_t val;
! 261:
! 262: val = va_arg(args, u_int);
! 263: check_buf(this, sizeof(val));
! 264: *(uint32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
! 265: this->buf.len += sizeof(val);
! 266: break;
! 267: }
! 268: /** chunk_t */
! 269: case HA_NONCE_I:
! 270: case HA_NONCE_R:
! 271: case HA_SECRET:
! 272: case HA_LOCAL_DH:
! 273: case HA_REMOTE_DH:
! 274: case HA_PSK:
! 275: case HA_IV:
! 276: case HA_OLD_SKD:
! 277: {
! 278: chunk_t chunk;
! 279:
! 280: chunk = va_arg(args, chunk_t);
! 281: check_buf(this, chunk.len + sizeof(uint16_t));
! 282: *(uint16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
! 283: memcpy(this->buf.ptr + this->buf.len + sizeof(uint16_t),
! 284: chunk.ptr, chunk.len);
! 285: this->buf.len += chunk.len + sizeof(uint16_t);;
! 286: break;
! 287: }
! 288: /** traffic_selector_t */
! 289: case HA_LOCAL_TS:
! 290: case HA_REMOTE_TS:
! 291: {
! 292: ts_encoding_t *enc;
! 293: traffic_selector_t *ts;
! 294: chunk_t data;
! 295:
! 296: ts = va_arg(args, traffic_selector_t*);
! 297: data = chunk_cata("cc", ts->get_from_address(ts),
! 298: ts->get_to_address(ts));
! 299: check_buf(this, sizeof(ts_encoding_t) + data.len);
! 300: enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
! 301: this->buf.len += sizeof(ts_encoding_t) + data.len;
! 302: enc->type = ts->get_type(ts);
! 303: enc->protocol = ts->get_protocol(ts);
! 304: enc->from_port = htons(ts->get_from_port(ts));
! 305: enc->to_port = htons(ts->get_to_port(ts));
! 306: enc->dynamic = ts->is_dynamic(ts);
! 307: memcpy(enc->encoding, data.ptr, data.len);
! 308: break;
! 309: }
! 310: default:
! 311: {
! 312: DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
! 313: this->buf.len -= sizeof(uint8_t);
! 314: break;
! 315: }
! 316: }
! 317: va_end(args);
! 318: }
! 319:
! 320: /**
! 321: * Attribute enumerator implementation
! 322: */
! 323: typedef struct {
! 324: /** implements enumerator_t */
! 325: enumerator_t public;
! 326: /** position in message */
! 327: chunk_t buf;
! 328: /** cleanup handler of current element, if any */
! 329: void (*cleanup)(void* data);
! 330: /** data to pass to cleanup handler */
! 331: void *cleanup_data;
! 332: } attribute_enumerator_t;
! 333:
! 334: METHOD(enumerator_t, attribute_enumerate, bool,
! 335: attribute_enumerator_t *this, va_list args)
! 336: {
! 337: ha_message_attribute_t attr, *attr_out;
! 338: ha_message_value_t *value;
! 339:
! 340: VA_ARGS_VGET(args, attr_out, value);
! 341:
! 342: if (this->cleanup)
! 343: {
! 344: this->cleanup(this->cleanup_data);
! 345: this->cleanup = NULL;
! 346: }
! 347: if (this->buf.len < 1)
! 348: {
! 349: return FALSE;
! 350: }
! 351: attr = this->buf.ptr[0];
! 352: this->buf = chunk_skip(this->buf, 1);
! 353: switch (attr)
! 354: {
! 355: /* ike_sa_id_t* */
! 356: case HA_IKE_ID:
! 357: case HA_IKE_REKEY_ID:
! 358: {
! 359: ike_sa_id_encoding_t *enc;
! 360:
! 361: if (this->buf.len < sizeof(ike_sa_id_encoding_t))
! 362: {
! 363: return FALSE;
! 364: }
! 365: enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
! 366: value->ike_sa_id = ike_sa_id_create(enc->ike_version,
! 367: enc->initiator_spi, enc->responder_spi,
! 368: enc->initiator);
! 369: *attr_out = attr;
! 370: this->cleanup = (void*)value->ike_sa_id->destroy;
! 371: this->cleanup_data = value->ike_sa_id;
! 372: this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
! 373: return TRUE;
! 374: }
! 375: /* identification_t* */
! 376: case HA_LOCAL_ID:
! 377: case HA_REMOTE_ID:
! 378: case HA_REMOTE_EAP_ID:
! 379: {
! 380: identification_encoding_t *enc;
! 381:
! 382: enc = (identification_encoding_t*)(this->buf.ptr);
! 383: if (this->buf.len < sizeof(identification_encoding_t) ||
! 384: this->buf.len < sizeof(identification_encoding_t) + enc->len)
! 385: {
! 386: return FALSE;
! 387: }
! 388: value->id = identification_create_from_encoding(enc->type,
! 389: chunk_create(enc->encoding, enc->len));
! 390: *attr_out = attr;
! 391: this->cleanup = (void*)value->id->destroy;
! 392: this->cleanup_data = value->id;
! 393: this->buf = chunk_skip(this->buf,
! 394: sizeof(identification_encoding_t) + enc->len);
! 395: return TRUE;
! 396: }
! 397: /* host_t* */
! 398: case HA_LOCAL_ADDR:
! 399: case HA_REMOTE_ADDR:
! 400: case HA_LOCAL_VIP:
! 401: case HA_REMOTE_VIP:
! 402: case HA_PEER_ADDR:
! 403: {
! 404: host_encoding_t *enc;
! 405:
! 406: enc = (host_encoding_t*)(this->buf.ptr);
! 407: if (this->buf.len < sizeof(host_encoding_t))
! 408: {
! 409: return FALSE;
! 410: }
! 411: value->host = host_create_from_chunk(enc->family,
! 412: chunk_create(enc->encoding,
! 413: this->buf.len - sizeof(host_encoding_t)),
! 414: ntohs(enc->port));
! 415: if (!value->host)
! 416: {
! 417: return FALSE;
! 418: }
! 419: *attr_out = attr;
! 420: this->cleanup = (void*)value->host->destroy;
! 421: this->cleanup_data = value->host;
! 422: this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
! 423: value->host->get_address(value->host).len);
! 424: return TRUE;
! 425: }
! 426: /* char* */
! 427: case HA_CONFIG_NAME:
! 428: {
! 429: size_t len;
! 430:
! 431: len = strnlen(this->buf.ptr, this->buf.len);
! 432: if (len >= this->buf.len)
! 433: {
! 434: return FALSE;
! 435: }
! 436: value->str = this->buf.ptr;
! 437: *attr_out = attr;
! 438: this->buf = chunk_skip(this->buf, len + 1);
! 439: return TRUE;
! 440: }
! 441: /* uint8_t */
! 442: case HA_IKE_VERSION:
! 443: case HA_INITIATOR:
! 444: case HA_IPSEC_MODE:
! 445: case HA_IPCOMP:
! 446: {
! 447: if (this->buf.len < sizeof(uint8_t))
! 448: {
! 449: return FALSE;
! 450: }
! 451: value->u8 = *(uint8_t*)this->buf.ptr;
! 452: *attr_out = attr;
! 453: this->buf = chunk_skip(this->buf, sizeof(uint8_t));
! 454: return TRUE;
! 455: }
! 456: /** uint16_t */
! 457: case HA_ALG_DH:
! 458: case HA_ALG_PRF:
! 459: case HA_ALG_OLD_PRF:
! 460: case HA_ALG_ENCR:
! 461: case HA_ALG_ENCR_LEN:
! 462: case HA_ALG_INTEG:
! 463: case HA_INBOUND_CPI:
! 464: case HA_OUTBOUND_CPI:
! 465: case HA_SEGMENT:
! 466: case HA_ESN:
! 467: case HA_AUTH_METHOD:
! 468: {
! 469: if (this->buf.len < sizeof(uint16_t))
! 470: {
! 471: return FALSE;
! 472: }
! 473: value->u16 = ntohs(*(uint16_t*)this->buf.ptr);
! 474: *attr_out = attr;
! 475: this->buf = chunk_skip(this->buf, sizeof(uint16_t));
! 476: return TRUE;
! 477: }
! 478: /** uint32_t */
! 479: case HA_CONDITIONS:
! 480: case HA_EXTENSIONS:
! 481: case HA_INBOUND_SPI:
! 482: case HA_OUTBOUND_SPI:
! 483: case HA_MID:
! 484: {
! 485: if (this->buf.len < sizeof(uint32_t))
! 486: {
! 487: return FALSE;
! 488: }
! 489: value->u32 = ntohl(*(uint32_t*)this->buf.ptr);
! 490: *attr_out = attr;
! 491: this->buf = chunk_skip(this->buf, sizeof(uint32_t));
! 492: return TRUE;
! 493: }
! 494: /** chunk_t */
! 495: case HA_NONCE_I:
! 496: case HA_NONCE_R:
! 497: case HA_SECRET:
! 498: case HA_LOCAL_DH:
! 499: case HA_REMOTE_DH:
! 500: case HA_PSK:
! 501: case HA_IV:
! 502: case HA_OLD_SKD:
! 503: {
! 504: size_t len;
! 505:
! 506: if (this->buf.len < sizeof(uint16_t))
! 507: {
! 508: return FALSE;
! 509: }
! 510: len = ntohs(*(uint16_t*)this->buf.ptr);
! 511: this->buf = chunk_skip(this->buf, sizeof(uint16_t));
! 512: if (this->buf.len < len)
! 513: {
! 514: return FALSE;
! 515: }
! 516: value->chunk.len = len;
! 517: value->chunk.ptr = this->buf.ptr;
! 518: *attr_out = attr;
! 519: this->buf = chunk_skip(this->buf, len);
! 520: return TRUE;
! 521: }
! 522: case HA_LOCAL_TS:
! 523: case HA_REMOTE_TS:
! 524: {
! 525: ts_encoding_t *enc;
! 526: host_t *host;
! 527: int addr_len;
! 528:
! 529: enc = (ts_encoding_t*)(this->buf.ptr);
! 530: if (this->buf.len < sizeof(ts_encoding_t))
! 531: {
! 532: return FALSE;
! 533: }
! 534: switch (enc->type)
! 535: {
! 536: case TS_IPV4_ADDR_RANGE:
! 537: addr_len = 4;
! 538: if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
! 539: {
! 540: return FALSE;
! 541: }
! 542: break;
! 543: case TS_IPV6_ADDR_RANGE:
! 544: addr_len = 16;
! 545: if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
! 546: {
! 547: return FALSE;
! 548: }
! 549: break;
! 550: default:
! 551: return FALSE;
! 552: }
! 553: if (enc->dynamic)
! 554: {
! 555: host = host_create_from_chunk(0,
! 556: chunk_create(enc->encoding, addr_len), 0);
! 557: if (!host)
! 558: {
! 559: return FALSE;
! 560: }
! 561: value->ts = traffic_selector_create_dynamic(enc->protocol,
! 562: ntohs(enc->from_port), ntohs(enc->to_port));
! 563: value->ts->set_address(value->ts, host);
! 564: host->destroy(host);
! 565: }
! 566: else
! 567: {
! 568: value->ts = traffic_selector_create_from_bytes(enc->protocol,
! 569: enc->type, chunk_create(enc->encoding, addr_len),
! 570: ntohs(enc->from_port),
! 571: chunk_create(enc->encoding + addr_len, addr_len),
! 572: ntohs(enc->to_port));
! 573: if (!value->ts)
! 574: {
! 575: return FALSE;
! 576: }
! 577: }
! 578: *attr_out = attr;
! 579: this->cleanup = (void*)value->ts->destroy;
! 580: this->cleanup_data = value->ts;
! 581: this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
! 582: + addr_len * 2);
! 583: return TRUE;
! 584: }
! 585: default:
! 586: {
! 587: return FALSE;
! 588: }
! 589: }
! 590: }
! 591:
! 592: METHOD(enumerator_t, enum_destroy, void,
! 593: attribute_enumerator_t *this)
! 594: {
! 595: if (this->cleanup)
! 596: {
! 597: this->cleanup(this->cleanup_data);
! 598: }
! 599: free(this);
! 600: }
! 601:
! 602: METHOD(ha_message_t, create_attribute_enumerator, enumerator_t*,
! 603: private_ha_message_t *this)
! 604: {
! 605: attribute_enumerator_t *e;
! 606:
! 607: INIT(e,
! 608: .public = {
! 609: .enumerate = enumerator_enumerate_default,
! 610: .venumerate = _attribute_enumerate,
! 611: .destroy = _enum_destroy,
! 612: },
! 613: .buf = chunk_skip(this->buf, 2),
! 614: );
! 615:
! 616: return &e->public;
! 617: }
! 618:
! 619: METHOD(ha_message_t, get_encoding, chunk_t,
! 620: private_ha_message_t *this)
! 621: {
! 622: return this->buf;
! 623: }
! 624:
! 625: METHOD(ha_message_t, destroy, void,
! 626: private_ha_message_t *this)
! 627: {
! 628: free(this->buf.ptr);
! 629: free(this);
! 630: }
! 631:
! 632:
! 633: static private_ha_message_t *ha_message_create_generic()
! 634: {
! 635: private_ha_message_t *this;
! 636:
! 637: INIT(this,
! 638: .public = {
! 639: .get_type = _get_type,
! 640: .add_attribute = _add_attribute,
! 641: .create_attribute_enumerator = _create_attribute_enumerator,
! 642: .get_encoding = _get_encoding,
! 643: .destroy = _destroy,
! 644: },
! 645: );
! 646: return this;
! 647: }
! 648:
! 649: /**
! 650: * See header
! 651: */
! 652: ha_message_t *ha_message_create(ha_message_type_t type)
! 653: {
! 654: private_ha_message_t *this = ha_message_create_generic();
! 655:
! 656: this->allocated = ALLOCATION_BLOCK;
! 657: this->buf.ptr = malloc(this->allocated);
! 658: this->buf.len = 2;
! 659: this->buf.ptr[0] = HA_MESSAGE_VERSION;
! 660: this->buf.ptr[1] = type;
! 661:
! 662: return &this->public;
! 663: }
! 664:
! 665: /**
! 666: * See header
! 667: */
! 668: ha_message_t *ha_message_parse(chunk_t data)
! 669: {
! 670: private_ha_message_t *this;
! 671:
! 672: if (data.len < 2)
! 673: {
! 674: DBG1(DBG_CFG, "HA message too short");
! 675: return NULL;
! 676: }
! 677: if (data.ptr[0] != HA_MESSAGE_VERSION)
! 678: {
! 679: DBG1(DBG_CFG, "HA message has version %d, expected %d",
! 680: data.ptr[0], HA_MESSAGE_VERSION);
! 681: return NULL;
! 682: }
! 683:
! 684: this = ha_message_create_generic();
! 685: this->buf = chunk_clone(data);
! 686: this->allocated = this->buf.len;
! 687:
! 688: return &this->public;
! 689: }
! 690:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>