1 /* 2 * lib/netfilter/exp.c Conntrack Expectation 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch> 10 * Copyright (c) 2007 Philip Craig <philipc (at) snapgear.com> 11 * Copyright (c) 2007 Secure Computing Corporation 12 * Copyright (c= 2008 Patrick McHardy <kaber (at) trash.net> 13 * Copyright (c) 2012 Rich Fought <rich.fought (at) watchguard.com> 14 */ 15 16 /** 17 * @ingroup nfnl 18 * @defgroup exp Expectation 19 * @brief 20 * @{ 21 */ 22 23 #include <byteswap.h> 24 #include <sys/types.h> 25 #include <linux/netfilter/nfnetlink_conntrack.h> 26 27 #include <netlink-private/netlink.h> 28 #include <netlink/attr.h> 29 #include <netlink/netfilter/nfnl.h> 30 #include <netlink/netfilter/exp.h> 31 32 static struct nl_cache_ops nfnl_exp_ops; 33 34 static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = { 35 [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, 36 [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, 37 [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, 38 [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, 39 [CTA_EXPECT_ID] = { .type = NLA_U32 }, 40 [CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING }, 41 [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, 42 [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37 43 [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5 44 [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5 45 [CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5 46 }; 47 48 static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = { 49 [CTA_TUPLE_IP] = { .type = NLA_NESTED }, 50 [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, 51 }; 52 53 static struct nla_policy exp_ip_policy[CTA_IP_MAX+1] = { 54 [CTA_IP_V4_SRC] = { .type = NLA_U32 }, 55 [CTA_IP_V4_DST] = { .type = NLA_U32 }, 56 [CTA_IP_V6_SRC] = { .minlen = 16 }, 57 [CTA_IP_V6_DST] = { .minlen = 16 }, 58 }; 59 60 static struct nla_policy exp_proto_policy[CTA_PROTO_MAX+1] = { 61 [CTA_PROTO_NUM] = { .type = NLA_U8 }, 62 [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, 63 [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, 64 [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, 65 [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, 66 [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, 67 [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, 68 [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, 69 [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, 70 }; 71 72 static struct nla_policy exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = { 73 [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 }, 74 [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED }, 75 }; 76 77 static int exp_parse_ip(struct nfnl_exp *exp, int tuple, struct nlattr *attr) 78 { 79 struct nlattr *tb[CTA_IP_MAX+1]; 80 struct nl_addr *addr; 81 int err; 82 83 err = nla_parse_nested(tb, CTA_IP_MAX, attr, exp_ip_policy); 84 if (err < 0) 85 goto errout; 86 87 if (tb[CTA_IP_V4_SRC]) { 88 addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET); 89 if (addr == NULL) 90 goto errout_enomem; 91 err = nfnl_exp_set_src(exp, tuple, addr); 92 nl_addr_put(addr); 93 if (err < 0) 94 goto errout; 95 } 96 if (tb[CTA_IP_V4_DST]) { 97 addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET); 98 if (addr == NULL) 99 goto errout_enomem; 100 err = nfnl_exp_set_dst(exp, tuple, addr); 101 nl_addr_put(addr); 102 if (err < 0) 103 goto errout; 104 } 105 if (tb[CTA_IP_V6_SRC]) { 106 addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6); 107 if (addr == NULL) 108 goto errout_enomem; 109 err = nfnl_exp_set_src(exp, tuple, addr); 110 nl_addr_put(addr); 111 if (err < 0) 112 goto errout; 113 } 114 if (tb[CTA_IP_V6_DST]) { 115 addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6); 116 if (addr == NULL) 117 goto errout_enomem; 118 err = nfnl_exp_set_dst(exp, tuple, addr); 119 nl_addr_put(addr); 120 if (err < 0) 121 goto errout; 122 } 123 124 return 0; 125 126 errout_enomem: 127 err = -NLE_NOMEM; 128 errout: 129 return err; 130 } 131 132 static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr) 133 { 134 struct nlattr *tb[CTA_PROTO_MAX+1]; 135 int err; 136 uint16_t srcport = 0, dstport = 0, icmpid = 0; 137 uint8_t icmptype = 0, icmpcode = 0; 138 139 err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy); 140 if (err < 0) 141 return err; 142 143 if (tb[CTA_PROTO_NUM]) 144 nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM])); 145 146 if (tb[CTA_PROTO_SRC_PORT]) 147 srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT])); 148 if (tb[CTA_PROTO_DST_PORT]) 149 dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])); 150 if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT]) 151 nfnl_exp_set_ports(exp, tuple, srcport, dstport); 152 153 if (tb[CTA_PROTO_ICMP_ID]) 154 icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])); 155 if (tb[CTA_PROTO_ICMP_TYPE]) 156 icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]); 157 if (tb[CTA_PROTO_ICMP_CODE]) 158 icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]); 159 if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE]) 160 nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode); 161 return 0; 162 } 163 164 static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr) 165 { 166 struct nlattr *tb[CTA_TUPLE_MAX+1]; 167 int err; 168 169 err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy); 170 if (err < 0) 171 return err; 172 173 if (tb[CTA_TUPLE_IP]) { 174 err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]); 175 if (err < 0) 176 return err; 177 } 178 179 if (tb[CTA_TUPLE_PROTO]) { 180 err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]); 181 if (err < 0) 182 return err; 183 } 184 185 return 0; 186 } 187 188 static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr) 189 { 190 struct nlattr *tb[CTA_EXPECT_NAT_MAX+1]; 191 int err; 192 193 err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy); 194 if (err < 0) 195 return err; 196 197 if (tb[CTA_EXPECT_NAT_DIR]) 198 nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR])); 199 200 if (tb[CTA_EXPECT_NAT_TUPLE]) { 201 err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]); 202 if (err < 0) 203 return err; 204 } 205 206 return 0; 207 } 208 209 int nfnlmsg_exp_group(struct nlmsghdr *nlh) 210 { 211 switch (nfnlmsg_subtype(nlh)) { 212 case IPCTNL_MSG_EXP_NEW: 213 if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) 214 return NFNLGRP_CONNTRACK_EXP_NEW; 215 else 216 return NFNLGRP_CONNTRACK_EXP_UPDATE; 217 case IPCTNL_MSG_EXP_DELETE: 218 return NFNLGRP_CONNTRACK_EXP_DESTROY; 219 default: 220 return NFNLGRP_NONE; 221 } 222 } 223 224 int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result) 225 { 226 struct nfnl_exp *exp; 227 struct nlattr *tb[CTA_MAX+1]; 228 int err; 229 230 exp = nfnl_exp_alloc(); 231 if (!exp) 232 return -NLE_NOMEM; 233 234 exp->ce_msgtype = nlh->nlmsg_type; 235 236 err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX, 237 exp_policy); 238 if (err < 0) 239 goto errout; 240 241 nfnl_exp_set_family(exp, nfnlmsg_family(nlh)); 242 243 if (tb[CTA_EXPECT_TUPLE]) { 244 err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]); 245 if (err < 0) 246 goto errout; 247 } 248 if (tb[CTA_EXPECT_MASTER]) { 249 err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]); 250 if (err < 0) 251 goto errout; 252 } 253 if (tb[CTA_EXPECT_MASK]) { 254 err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]); 255 if (err < 0) 256 goto errout; 257 } 258 259 if (tb[CTA_EXPECT_NAT]) { 260 err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]); 261 if (err < 0) 262 goto errout; 263 } 264 265 if (tb[CTA_EXPECT_CLASS]) 266 nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS]))); 267 268 if (tb[CTA_EXPECT_FN]) 269 nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN])); 270 271 if (tb[CTA_EXPECT_TIMEOUT]) 272 nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT]))); 273 274 if (tb[CTA_EXPECT_ID]) 275 nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID]))); 276 277 if (tb[CTA_EXPECT_HELP_NAME]) 278 nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME])); 279 280 if (tb[CTA_EXPECT_ZONE]) 281 nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE]))); 282 283 if (tb[CTA_EXPECT_FLAGS]) 284 nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS]))); 285 286 *result = exp; 287 return 0; 288 289 errout: 290 nfnl_exp_put(exp); 291 return err; 292 } 293 294 static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, 295 struct nlmsghdr *nlh, struct nl_parser_param *pp) 296 { 297 struct nfnl_exp *exp; 298 int err; 299 300 if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0) 301 return err; 302 303 err = pp->pp_cb((struct nl_object *) exp, pp); 304 nfnl_exp_put(exp); 305 return err; 306 } 307 308 int nfnl_exp_dump_request(struct nl_sock *sk) 309 { 310 return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET, 311 NLM_F_DUMP, AF_UNSPEC, 0); 312 } 313 314 static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk) 315 { 316 return nfnl_exp_dump_request(sk); 317 } 318 319 static int exp_get_tuple_attr(int tuple) 320 { 321 int attr = 0; 322 323 switch (tuple) { 324 case CTA_EXPECT_MASTER: 325 attr = NFNL_EXP_TUPLE_MASTER; 326 break; 327 case CTA_EXPECT_MASK: 328 attr = NFNL_EXP_TUPLE_MASK; 329 break; 330 case CTA_EXPECT_NAT: 331 attr = NFNL_EXP_TUPLE_NAT; 332 break; 333 case CTA_EXPECT_TUPLE: 334 default : 335 attr = NFNL_EXP_TUPLE_EXPECT; 336 break; 337 } 338 339 return attr; 340 } 341 342 static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp, 343 int cta) 344 { 345 struct nlattr *tuple, *ip, *proto; 346 struct nl_addr *addr; 347 int family; 348 349 family = nfnl_exp_get_family(exp); 350 351 int type = exp_get_tuple_attr(cta); 352 353 if (cta == CTA_EXPECT_NAT) 354 tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE); 355 else 356 tuple = nla_nest_start(msg, cta); 357 358 if (!tuple) 359 goto nla_put_failure; 360 361 ip = nla_nest_start(msg, CTA_TUPLE_IP); 362 if (!ip) 363 goto nla_put_failure; 364 365 addr = nfnl_exp_get_src(exp, type); 366 if (addr) 367 NLA_PUT_ADDR(msg, 368 family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC, 369 addr); 370 371 addr = nfnl_exp_get_dst(exp, type); 372 if (addr) 373 NLA_PUT_ADDR(msg, 374 family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST, 375 addr); 376 377 nla_nest_end(msg, ip); 378 379 proto = nla_nest_start(msg, CTA_TUPLE_PROTO); 380 if (!proto) 381 goto nla_put_failure; 382 383 if (nfnl_exp_test_l4protonum(exp, type)) 384 NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type)); 385 386 if (nfnl_exp_test_ports(exp, type)) { 387 NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT, 388 htons(nfnl_exp_get_src_port(exp, type))); 389 390 NLA_PUT_U16(msg, CTA_PROTO_DST_PORT, 391 htons(nfnl_exp_get_dst_port(exp, type))); 392 } 393 394 if (nfnl_exp_test_icmp(exp, type)) { 395 NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID, 396 htons(nfnl_exp_get_icmp_id(exp, type))); 397 398 NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE, 399 nfnl_exp_get_icmp_type(exp, type)); 400 401 NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE, 402 nfnl_exp_get_icmp_code(exp, type)); 403 } 404 405 nla_nest_end(msg, proto); 406 407 nla_nest_end(msg, tuple); 408 return 0; 409 410 nla_put_failure: 411 return -NLE_MSGSIZE; 412 } 413 414 static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp) 415 { 416 struct nlattr *nat; 417 int err; 418 419 nat = nla_nest_start(msg, CTA_EXPECT_NAT); 420 421 if (nfnl_exp_test_nat_dir(exp)) { 422 NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR, 423 nfnl_exp_get_nat_dir(exp)); 424 } 425 426 if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT)) < 0) 427 goto nla_put_failure; 428 429 nla_nest_end(msg, nat); 430 return 0; 431 432 nla_put_failure: 433 return -NLE_MSGSIZE; 434 } 435 436 static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags, 437 struct nl_msg **result) 438 { 439 struct nl_msg *msg; 440 int err; 441 442 msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags, 443 nfnl_exp_get_family(exp), 0); 444 if (msg == NULL) 445 return -NLE_NOMEM; 446 447 if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0) 448 goto err_out; 449 450 if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0) 451 goto err_out; 452 453 if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0) 454 goto err_out; 455 456 if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) { 457 if ((err = nfnl_exp_build_nat(msg, exp)) < 0) 458 goto err_out; 459 } 460 461 if (nfnl_exp_test_class(exp)) 462 NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp))); 463 464 if (nfnl_exp_test_fn(exp)) 465 NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp)); 466 467 if (nfnl_exp_test_id(exp)) 468 NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp))); 469 470 if (nfnl_exp_test_timeout(exp)) 471 NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp))); 472 473 if (nfnl_exp_test_helper_name(exp)) 474 NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp)); 475 476 if (nfnl_exp_test_zone(exp)) 477 NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp))); 478 479 if (nfnl_exp_test_flags(exp)) 480 NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp))); 481 482 *result = msg; 483 return 0; 484 485 nla_put_failure: 486 err_out: 487 nlmsg_free(msg); 488 return err; 489 } 490 491 int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags, 492 struct nl_msg **result) 493 { 494 return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result); 495 } 496 497 int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) 498 { 499 struct nl_msg *msg; 500 int err; 501 502 if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0) 503 return err; 504 505 err = nl_send_auto_complete(sk, msg); 506 nlmsg_free(msg); 507 if (err < 0) 508 return err; 509 510 return wait_for_ack(sk); 511 } 512 513 int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags, 514 struct nl_msg **result) 515 { 516 return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result); 517 } 518 519 int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) 520 { 521 struct nl_msg *msg; 522 int err; 523 524 if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0) 525 return err; 526 527 err = nl_send_auto_complete(sk, msg); 528 nlmsg_free(msg); 529 if (err < 0) 530 return err; 531 532 return wait_for_ack(sk); 533 } 534 535 int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags, 536 struct nl_msg **result) 537 { 538 return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result); 539 } 540 541 int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags) 542 { 543 struct nl_msg *msg; 544 int err; 545 546 if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0) 547 return err; 548 549 err = nl_send_auto_complete(sk, msg); 550 nlmsg_free(msg); 551 if (err < 0) 552 return err; 553 554 return wait_for_ack(sk); 555 } 556 557 /** 558 * @name Cache Management 559 * @{ 560 */ 561 562 /** 563 * Build a expectation cache holding all expectations currently in the kernel 564 * @arg sk Netlink socket. 565 * @arg result Pointer to store resulting cache. 566 * 567 * Allocates a new cache, initializes it properly and updates it to 568 * contain all expectations currently in the kernel. 569 * 570 * @return 0 on success or a negative error code. 571 */ 572 int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result) 573 { 574 return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result); 575 } 576 577 /** @} */ 578 579 /** 580 * @name Expectation Addition 581 * @{ 582 */ 583 584 /** @} */ 585 586 static struct nl_af_group exp_groups[] = { 587 { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW }, 588 { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE }, 589 { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY }, 590 { END_OF_GROUP_LIST }, 591 }; 592 593 #define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type)) 594 static struct nl_cache_ops nfnl_exp_ops = { 595 .co_name = "netfilter/exp", 596 .co_hdrsize = NFNL_HDRLEN, 597 .co_msgtypes = { 598 { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" }, 599 { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" }, 600 { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" }, 601 END_OF_MSGTYPES_LIST, 602 }, 603 .co_protocol = NETLINK_NETFILTER, 604 .co_groups = exp_groups, 605 .co_request_update = exp_request_update, 606 .co_msg_parser = exp_msg_parser, 607 .co_obj_ops = &exp_obj_ops, 608 }; 609 610 static void __init exp_init(void) 611 { 612 nl_cache_mngt_register(&nfnl_exp_ops); 613 } 614 615 static void __exit exp_exit(void) 616 { 617 nl_cache_mngt_unregister(&nfnl_exp_ops); 618 } 619 620 /** @} */ 621