Annotation of embedaddon/strongswan/src/libcharon/config/child_cfg.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2008-2019 Tobias Brunner
! 3: * Copyright (C) 2016 Andreas Steffen
! 4: * Copyright (C) 2005-2007 Martin Willi
! 5: * Copyright (C) 2005 Jan Hutter
! 6: * HSR Hochschule fuer Technik Rapperswil
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify it
! 9: * under the terms of the GNU General Public License as published by the
! 10: * Free Software Foundation; either version 2 of the License, or (at your
! 11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 12: *
! 13: * This program is distributed in the hope that it will be useful, but
! 14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 16: * for more details.
! 17: */
! 18:
! 19: #include "child_cfg.h"
! 20:
! 21: #include <stdint.h>
! 22:
! 23: #include <daemon.h>
! 24:
! 25: ENUM(action_names, ACTION_NONE, ACTION_RESTART,
! 26: "clear",
! 27: "hold",
! 28: "restart",
! 29: );
! 30:
! 31: /** Default replay window size, if not set using charon.replay_window */
! 32: #define DEFAULT_REPLAY_WINDOW 32
! 33:
! 34: typedef struct private_child_cfg_t private_child_cfg_t;
! 35:
! 36: /**
! 37: * Private data of an child_cfg_t object
! 38: */
! 39: struct private_child_cfg_t {
! 40:
! 41: /**
! 42: * Public part
! 43: */
! 44: child_cfg_t public;
! 45:
! 46: /**
! 47: * Number of references hold by others to this child_cfg
! 48: */
! 49: refcount_t refcount;
! 50:
! 51: /**
! 52: * Name of the child_cfg, used to query it
! 53: */
! 54: char *name;
! 55:
! 56: /**
! 57: * Options
! 58: */
! 59: child_cfg_option_t options;
! 60:
! 61: /**
! 62: * list for all proposals
! 63: */
! 64: linked_list_t *proposals;
! 65:
! 66: /**
! 67: * list for traffic selectors for my site
! 68: */
! 69: linked_list_t *my_ts;
! 70:
! 71: /**
! 72: * list for traffic selectors for others site
! 73: */
! 74: linked_list_t *other_ts;
! 75:
! 76: /**
! 77: * updown script
! 78: */
! 79: char *updown;
! 80:
! 81: /**
! 82: * Mode to propose for a initiated CHILD: tunnel/transport
! 83: */
! 84: ipsec_mode_t mode;
! 85:
! 86: /**
! 87: * action to take to start CHILD_SA
! 88: */
! 89: action_t start_action;
! 90:
! 91: /**
! 92: * action to take on DPD
! 93: */
! 94: action_t dpd_action;
! 95:
! 96: /**
! 97: * action to take on CHILD_SA close
! 98: */
! 99: action_t close_action;
! 100:
! 101: /**
! 102: * CHILD_SA lifetime config
! 103: */
! 104: lifetime_cfg_t lifetime;
! 105:
! 106: /**
! 107: * Inactivity timeout
! 108: */
! 109: uint32_t inactivity;
! 110:
! 111: /**
! 112: * Reqid to install CHILD_SA with
! 113: */
! 114: uint32_t reqid;
! 115:
! 116: /**
! 117: * Optional interface ID to use for inbound CHILD_SA
! 118: */
! 119: uint32_t if_id_in;
! 120:
! 121: /**
! 122: * Optional interface ID to use for outbound CHILD_SA
! 123: */
! 124: uint32_t if_id_out;
! 125:
! 126: /**
! 127: * Optional mark to install inbound CHILD_SA with
! 128: */
! 129: mark_t mark_in;
! 130:
! 131: /**
! 132: * Optional mark to install outbound CHILD_SA with
! 133: */
! 134: mark_t mark_out;
! 135:
! 136: /**
! 137: * Optional mark to set to packets after inbound processing
! 138: */
! 139: mark_t set_mark_in;
! 140:
! 141: /**
! 142: * Optional mark to set to packets after outbound processing
! 143: */
! 144: mark_t set_mark_out;
! 145:
! 146: /**
! 147: * Traffic Flow Confidentiality padding, if enabled
! 148: */
! 149: uint32_t tfc;
! 150:
! 151: /**
! 152: * Optional manually-set IPsec policy priorities
! 153: */
! 154: uint32_t manual_prio;
! 155:
! 156: /**
! 157: * Optional restriction of IPsec policy to a given network interface
! 158: */
! 159: char *interface;
! 160:
! 161: /**
! 162: * anti-replay window size
! 163: */
! 164: uint32_t replay_window;
! 165:
! 166: /**
! 167: * HW offload mode
! 168: */
! 169: hw_offload_t hw_offload;
! 170:
! 171: /**
! 172: * DS header field copy mode
! 173: */
! 174: dscp_copy_t copy_dscp;
! 175: };
! 176:
! 177: METHOD(child_cfg_t, get_name, char*,
! 178: private_child_cfg_t *this)
! 179: {
! 180: return this->name;
! 181: }
! 182:
! 183: METHOD(child_cfg_t, has_option, bool,
! 184: private_child_cfg_t *this, child_cfg_option_t option)
! 185: {
! 186: return this->options & option;
! 187: }
! 188:
! 189: METHOD(child_cfg_t, add_proposal, void,
! 190: private_child_cfg_t *this, proposal_t *proposal)
! 191: {
! 192: if (proposal)
! 193: {
! 194: this->proposals->insert_last(this->proposals, proposal);
! 195: }
! 196: }
! 197:
! 198: CALLBACK(match_proposal, bool,
! 199: proposal_t *item, va_list args)
! 200: {
! 201: proposal_t *proposal;
! 202:
! 203: VA_ARGS_VGET(args, proposal);
! 204: return item->equals(item, proposal);
! 205: }
! 206:
! 207: METHOD(child_cfg_t, get_proposals, linked_list_t*,
! 208: private_child_cfg_t *this, bool strip_dh)
! 209: {
! 210: enumerator_t *enumerator;
! 211: proposal_t *current;
! 212: proposal_selection_flag_t flags = 0;
! 213: linked_list_t *proposals = linked_list_create();
! 214:
! 215: if (strip_dh)
! 216: {
! 217: flags |= PROPOSAL_SKIP_DH;
! 218: }
! 219:
! 220: enumerator = this->proposals->create_enumerator(this->proposals);
! 221: while (enumerator->enumerate(enumerator, ¤t))
! 222: {
! 223: current = current->clone(current, flags);
! 224: if (proposals->find_first(proposals, match_proposal, NULL, current))
! 225: {
! 226: current->destroy(current);
! 227: continue;
! 228: }
! 229: proposals->insert_last(proposals, current);
! 230: }
! 231: enumerator->destroy(enumerator);
! 232:
! 233: DBG2(DBG_CFG, "configured proposals: %#P", proposals);
! 234:
! 235: return proposals;
! 236: }
! 237:
! 238: METHOD(child_cfg_t, select_proposal, proposal_t*,
! 239: private_child_cfg_t*this, linked_list_t *proposals,
! 240: proposal_selection_flag_t flags)
! 241: {
! 242: return proposal_select(this->proposals, proposals, flags);
! 243: }
! 244:
! 245: METHOD(child_cfg_t, add_traffic_selector, void,
! 246: private_child_cfg_t *this, bool local, traffic_selector_t *ts)
! 247: {
! 248: if (local)
! 249: {
! 250: this->my_ts->insert_last(this->my_ts, ts);
! 251: }
! 252: else
! 253: {
! 254: this->other_ts->insert_last(this->other_ts, ts);
! 255: }
! 256: }
! 257:
! 258: METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
! 259: private_child_cfg_t *this, bool local, linked_list_t *supplied,
! 260: linked_list_t *hosts, bool log)
! 261: {
! 262: enumerator_t *e1, *e2;
! 263: traffic_selector_t *ts1, *ts2, *selected;
! 264: linked_list_t *result, *derived;
! 265: host_t *host;
! 266:
! 267: result = linked_list_create();
! 268: derived = linked_list_create();
! 269: if (local)
! 270: {
! 271: e1 = this->my_ts->create_enumerator(this->my_ts);
! 272: }
! 273: else
! 274: {
! 275: e1 = this->other_ts->create_enumerator(this->other_ts);
! 276: }
! 277: /* in a first step, replace "dynamic" TS with the host list */
! 278: while (e1->enumerate(e1, &ts1))
! 279: {
! 280: if (hosts && hosts->get_count(hosts))
! 281: { /* set hosts if TS is dynamic or as initiator in transport mode */
! 282: bool dynamic = ts1->is_dynamic(ts1),
! 283: proxy_mode = has_option(this, OPT_PROXY_MODE);
! 284: if (dynamic || (this->mode == MODE_TRANSPORT && !proxy_mode &&
! 285: !supplied))
! 286: {
! 287: e2 = hosts->create_enumerator(hosts);
! 288: while (e2->enumerate(e2, &host))
! 289: {
! 290: ts2 = ts1->clone(ts1);
! 291: if (dynamic || !host->is_anyaddr(host))
! 292: { /* don't make regular TS larger than they were */
! 293: ts2->set_address(ts2, host);
! 294: }
! 295: derived->insert_last(derived, ts2);
! 296: }
! 297: e2->destroy(e2);
! 298: continue;
! 299: }
! 300: }
! 301: derived->insert_last(derived, ts1->clone(ts1));
! 302: }
! 303: e1->destroy(e1);
! 304:
! 305: if (log)
! 306: {
! 307: DBG2(DBG_CFG, "%s traffic selectors for %s:",
! 308: supplied ? "selecting" : "proposing", local ? "us" : "other");
! 309: }
! 310: if (!supplied)
! 311: {
! 312: while (derived->remove_first(derived, (void**)&ts1) == SUCCESS)
! 313: {
! 314: if (log)
! 315: {
! 316: DBG2(DBG_CFG, " %R", ts1);
! 317: }
! 318: result->insert_last(result, ts1);
! 319: }
! 320: derived->destroy(derived);
! 321: }
! 322: else
! 323: {
! 324: e1 = derived->create_enumerator(derived);
! 325: e2 = supplied->create_enumerator(supplied);
! 326: /* enumerate all configured/derived selectors */
! 327: while (e1->enumerate(e1, &ts1))
! 328: {
! 329: /* enumerate all supplied traffic selectors */
! 330: while (e2->enumerate(e2, &ts2))
! 331: {
! 332: selected = ts1->get_subset(ts1, ts2);
! 333: if (selected)
! 334: {
! 335: if (log)
! 336: {
! 337: DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
! 338: ts1, ts2, selected);
! 339: }
! 340: result->insert_last(result, selected);
! 341: }
! 342: else if (log)
! 343: {
! 344: DBG2(DBG_CFG, " config: %R, received: %R => no match",
! 345: ts1, ts2);
! 346: }
! 347: }
! 348: supplied->reset_enumerator(supplied, e2);
! 349: }
! 350: e1->destroy(e1);
! 351: e2->destroy(e2);
! 352:
! 353: /* check if we/peer did any narrowing, raise alert */
! 354: e1 = derived->create_enumerator(derived);
! 355: e2 = result->create_enumerator(result);
! 356: while (e1->enumerate(e1, &ts1))
! 357: {
! 358: if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
! 359: {
! 360: charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
! 361: local, result, this);
! 362: break;
! 363: }
! 364: }
! 365: e1->destroy(e1);
! 366: e2->destroy(e2);
! 367:
! 368: derived->destroy_offset(derived, offsetof(traffic_selector_t, destroy));
! 369: }
! 370:
! 371: /* remove any redundant traffic selectors in the list */
! 372: e1 = result->create_enumerator(result);
! 373: e2 = result->create_enumerator(result);
! 374: while (e1->enumerate(e1, &ts1))
! 375: {
! 376: while (e2->enumerate(e2, &ts2))
! 377: {
! 378: if (ts1 != ts2)
! 379: {
! 380: if (ts2->is_contained_in(ts2, ts1))
! 381: {
! 382: result->remove_at(result, e2);
! 383: ts2->destroy(ts2);
! 384: result->reset_enumerator(result, e1);
! 385: break;
! 386: }
! 387: if (ts1->is_contained_in(ts1, ts2))
! 388: {
! 389: result->remove_at(result, e1);
! 390: ts1->destroy(ts1);
! 391: break;
! 392: }
! 393: }
! 394: }
! 395: result->reset_enumerator(result, e2);
! 396: }
! 397: e1->destroy(e1);
! 398: e2->destroy(e2);
! 399:
! 400: return result;
! 401: }
! 402:
! 403: METHOD(child_cfg_t, get_updown, char*,
! 404: private_child_cfg_t *this)
! 405: {
! 406: return this->updown;
! 407: }
! 408:
! 409: /**
! 410: * Applies jitter to the rekey value. Returns the new rekey value.
! 411: * Note: The distribution of random values is not perfect, but it
! 412: * should get the job done.
! 413: */
! 414: static uint64_t apply_jitter(uint64_t rekey, uint64_t jitter)
! 415: {
! 416: if (jitter == 0)
! 417: {
! 418: return rekey;
! 419: }
! 420: jitter = (jitter == UINT64_MAX) ? jitter : jitter + 1;
! 421: return rekey - jitter * (random() / (RAND_MAX + 1.0));
! 422: }
! 423: #define APPLY_JITTER(l) l.rekey = apply_jitter(l.rekey, l.jitter)
! 424:
! 425: METHOD(child_cfg_t, get_lifetime, lifetime_cfg_t*,
! 426: private_child_cfg_t *this, bool jitter)
! 427: {
! 428: lifetime_cfg_t *lft = malloc_thing(lifetime_cfg_t);
! 429: memcpy(lft, &this->lifetime, sizeof(lifetime_cfg_t));
! 430: if (!jitter)
! 431: {
! 432: lft->time.jitter = lft->bytes.jitter = lft->packets.jitter = 0;
! 433: }
! 434: APPLY_JITTER(lft->time);
! 435: APPLY_JITTER(lft->bytes);
! 436: APPLY_JITTER(lft->packets);
! 437: return lft;
! 438: }
! 439:
! 440: METHOD(child_cfg_t, get_mode, ipsec_mode_t,
! 441: private_child_cfg_t *this)
! 442: {
! 443: return this->mode;
! 444: }
! 445:
! 446: METHOD(child_cfg_t, get_start_action, action_t,
! 447: private_child_cfg_t *this)
! 448: {
! 449: return this->start_action;
! 450: }
! 451:
! 452: METHOD(child_cfg_t, get_hw_offload, hw_offload_t,
! 453: private_child_cfg_t *this)
! 454: {
! 455: return this->hw_offload;
! 456: }
! 457:
! 458: METHOD(child_cfg_t, get_copy_dscp, dscp_copy_t,
! 459: private_child_cfg_t *this)
! 460: {
! 461: return this->copy_dscp;
! 462: }
! 463:
! 464: METHOD(child_cfg_t, get_dpd_action, action_t,
! 465: private_child_cfg_t *this)
! 466: {
! 467: return this->dpd_action;
! 468: }
! 469:
! 470: METHOD(child_cfg_t, get_close_action, action_t,
! 471: private_child_cfg_t *this)
! 472: {
! 473: return this->close_action;
! 474: }
! 475:
! 476: METHOD(child_cfg_t, get_dh_group, diffie_hellman_group_t,
! 477: private_child_cfg_t *this)
! 478: {
! 479: enumerator_t *enumerator;
! 480: proposal_t *proposal;
! 481: uint16_t dh_group = MODP_NONE;
! 482:
! 483: enumerator = this->proposals->create_enumerator(this->proposals);
! 484: while (enumerator->enumerate(enumerator, &proposal))
! 485: {
! 486: if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &dh_group, NULL))
! 487: {
! 488: break;
! 489: }
! 490: }
! 491: enumerator->destroy(enumerator);
! 492: return dh_group;
! 493: }
! 494:
! 495: METHOD(child_cfg_t, get_inactivity, uint32_t,
! 496: private_child_cfg_t *this)
! 497: {
! 498: return this->inactivity;
! 499: }
! 500:
! 501: METHOD(child_cfg_t, get_reqid, uint32_t,
! 502: private_child_cfg_t *this)
! 503: {
! 504: return this->reqid;
! 505: }
! 506:
! 507: METHOD(child_cfg_t, get_if_id, uint32_t,
! 508: private_child_cfg_t *this, bool inbound)
! 509: {
! 510: return inbound ? this->if_id_in : this->if_id_out;
! 511: }
! 512:
! 513: METHOD(child_cfg_t, get_mark, mark_t,
! 514: private_child_cfg_t *this, bool inbound)
! 515: {
! 516: return inbound ? this->mark_in : this->mark_out;
! 517: }
! 518:
! 519: METHOD(child_cfg_t, get_set_mark, mark_t,
! 520: private_child_cfg_t *this, bool inbound)
! 521: {
! 522: return inbound ? this->set_mark_in : this->set_mark_out;
! 523: }
! 524:
! 525: METHOD(child_cfg_t, get_tfc, uint32_t,
! 526: private_child_cfg_t *this)
! 527: {
! 528: return this->tfc;
! 529: }
! 530:
! 531: METHOD(child_cfg_t, get_manual_prio, uint32_t,
! 532: private_child_cfg_t *this)
! 533: {
! 534: return this->manual_prio;
! 535: }
! 536:
! 537: METHOD(child_cfg_t, get_interface, char*,
! 538: private_child_cfg_t *this)
! 539: {
! 540: return this->interface;
! 541: }
! 542:
! 543: METHOD(child_cfg_t, get_replay_window, uint32_t,
! 544: private_child_cfg_t *this)
! 545: {
! 546: return this->replay_window;
! 547: }
! 548:
! 549: METHOD(child_cfg_t, set_replay_window, void,
! 550: private_child_cfg_t *this, uint32_t replay_window)
! 551: {
! 552: this->replay_window = replay_window;
! 553: }
! 554:
! 555: #define LT_PART_EQUALS(a, b) ({ a.life == b.life && a.rekey == b.rekey && a.jitter == b.jitter; })
! 556: #define LIFETIME_EQUALS(a, b) ({ LT_PART_EQUALS(a.time, b.time) && LT_PART_EQUALS(a.bytes, b.bytes) && LT_PART_EQUALS(a.packets, b.packets); })
! 557:
! 558: METHOD(child_cfg_t, equals, bool,
! 559: private_child_cfg_t *this, child_cfg_t *other_pub)
! 560: {
! 561: private_child_cfg_t *other = (private_child_cfg_t*)other_pub;
! 562:
! 563: if (this == other)
! 564: {
! 565: return TRUE;
! 566: }
! 567: if (this->public.equals != other->public.equals)
! 568: {
! 569: return FALSE;
! 570: }
! 571: if (!this->proposals->equals_offset(this->proposals, other->proposals,
! 572: offsetof(proposal_t, equals)))
! 573: {
! 574: return FALSE;
! 575: }
! 576: if (!this->my_ts->equals_offset(this->my_ts, other->my_ts,
! 577: offsetof(traffic_selector_t, equals)))
! 578: {
! 579: return FALSE;
! 580: }
! 581: if (!this->other_ts->equals_offset(this->other_ts, other->other_ts,
! 582: offsetof(traffic_selector_t, equals)))
! 583: {
! 584: return FALSE;
! 585: }
! 586: return this->options == other->options &&
! 587: this->mode == other->mode &&
! 588: this->start_action == other->start_action &&
! 589: this->dpd_action == other->dpd_action &&
! 590: this->close_action == other->close_action &&
! 591: LIFETIME_EQUALS(this->lifetime, other->lifetime) &&
! 592: this->inactivity == other->inactivity &&
! 593: this->reqid == other->reqid &&
! 594: this->if_id_in == other->if_id_in &&
! 595: this->if_id_out == other->if_id_out &&
! 596: this->mark_in.value == other->mark_in.value &&
! 597: this->mark_in.mask == other->mark_in.mask &&
! 598: this->mark_out.value == other->mark_out.value &&
! 599: this->mark_out.mask == other->mark_out.mask &&
! 600: this->set_mark_in.value == other->set_mark_in.value &&
! 601: this->set_mark_in.mask == other->set_mark_in.mask &&
! 602: this->set_mark_out.value == other->set_mark_out.value &&
! 603: this->set_mark_out.mask == other->set_mark_out.mask &&
! 604: this->tfc == other->tfc &&
! 605: this->manual_prio == other->manual_prio &&
! 606: this->replay_window == other->replay_window &&
! 607: this->hw_offload == other->hw_offload &&
! 608: this->copy_dscp == other->copy_dscp &&
! 609: streq(this->updown, other->updown) &&
! 610: streq(this->interface, other->interface);
! 611: }
! 612:
! 613: METHOD(child_cfg_t, get_ref, child_cfg_t*,
! 614: private_child_cfg_t *this)
! 615: {
! 616: ref_get(&this->refcount);
! 617: return &this->public;
! 618: }
! 619:
! 620: METHOD(child_cfg_t, destroy, void,
! 621: private_child_cfg_t *this)
! 622: {
! 623: if (ref_put(&this->refcount))
! 624: {
! 625: this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
! 626: this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
! 627: this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
! 628: free(this->updown);
! 629: free(this->interface);
! 630: free(this->name);
! 631: free(this);
! 632: }
! 633: }
! 634:
! 635: /*
! 636: * Described in header-file
! 637: */
! 638: child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
! 639: {
! 640: private_child_cfg_t *this;
! 641:
! 642: INIT(this,
! 643: .public = {
! 644: .get_name = _get_name,
! 645: .add_traffic_selector = _add_traffic_selector,
! 646: .get_traffic_selectors = _get_traffic_selectors,
! 647: .add_proposal = _add_proposal,
! 648: .get_proposals = _get_proposals,
! 649: .select_proposal = _select_proposal,
! 650: .get_updown = _get_updown,
! 651: .get_mode = _get_mode,
! 652: .get_start_action = _get_start_action,
! 653: .get_dpd_action = _get_dpd_action,
! 654: .get_close_action = _get_close_action,
! 655: .get_lifetime = _get_lifetime,
! 656: .get_dh_group = _get_dh_group,
! 657: .get_inactivity = _get_inactivity,
! 658: .get_reqid = _get_reqid,
! 659: .get_if_id = _get_if_id,
! 660: .get_mark = _get_mark,
! 661: .get_set_mark = _get_set_mark,
! 662: .get_tfc = _get_tfc,
! 663: .get_manual_prio = _get_manual_prio,
! 664: .get_interface = _get_interface,
! 665: .get_replay_window = _get_replay_window,
! 666: .set_replay_window = _set_replay_window,
! 667: .has_option = _has_option,
! 668: .equals = _equals,
! 669: .get_ref = _get_ref,
! 670: .destroy = _destroy,
! 671: .get_hw_offload = _get_hw_offload,
! 672: .get_copy_dscp = _get_copy_dscp,
! 673: },
! 674: .name = strdup(name),
! 675: .options = data->options,
! 676: .updown = strdupnull(data->updown),
! 677: .reqid = data->reqid,
! 678: .if_id_in = data->if_id_in,
! 679: .if_id_out = data->if_id_out,
! 680: .mode = data->mode,
! 681: .start_action = data->start_action,
! 682: .dpd_action = data->dpd_action,
! 683: .close_action = data->close_action,
! 684: .mark_in = data->mark_in,
! 685: .mark_out = data->mark_out,
! 686: .set_mark_in = data->set_mark_in,
! 687: .set_mark_out = data->set_mark_out,
! 688: .lifetime = data->lifetime,
! 689: .inactivity = data->inactivity,
! 690: .tfc = data->tfc,
! 691: .manual_prio = data->priority,
! 692: .interface = strdupnull(data->interface),
! 693: .refcount = 1,
! 694: .proposals = linked_list_create(),
! 695: .my_ts = linked_list_create(),
! 696: .other_ts = linked_list_create(),
! 697: .replay_window = lib->settings->get_int(lib->settings,
! 698: "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
! 699: .hw_offload = data->hw_offload,
! 700: .copy_dscp = data->copy_dscp,
! 701: );
! 702:
! 703: return &this->public;
! 704: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>