diff options
author | Julius Volz <juliusv@google.com> | 2008-08-14 08:08:44 -0400 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2008-08-14 19:26:14 -0400 |
commit | 9a812198ae49967f239789164c55ec3e72b7e0dd (patch) | |
tree | 556991d2dbd8bba59e471ce20b9cd07b05656ef8 /net | |
parent | c1bc667e844c2677cdf927102ab384fe7b033762 (diff) |
IPVS: Add genetlink interface implementation
Add the implementation of the new Generic Netlink interface to IPVS and
keep the old set/getsockopt interface for userspace backwards
compatibility.
Signed-off-by: Julius Volz <juliusv@google.com>
Acked-by: Sven Wegener <sven.wegener@stealer.net>
Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_ctl.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index 6379705a8dcb..d1dbd8b311b7 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <net/ip.h> | 37 | #include <net/ip.h> |
38 | #include <net/route.h> | 38 | #include <net/route.h> |
39 | #include <net/sock.h> | 39 | #include <net/sock.h> |
40 | #include <net/genetlink.h> | ||
40 | 41 | ||
41 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
42 | 43 | ||
@@ -2320,6 +2321,872 @@ static struct nf_sockopt_ops ip_vs_sockopts = { | |||
2320 | .owner = THIS_MODULE, | 2321 | .owner = THIS_MODULE, |
2321 | }; | 2322 | }; |
2322 | 2323 | ||
2324 | /* | ||
2325 | * Generic Netlink interface | ||
2326 | */ | ||
2327 | |||
2328 | /* IPVS genetlink family */ | ||
2329 | static struct genl_family ip_vs_genl_family = { | ||
2330 | .id = GENL_ID_GENERATE, | ||
2331 | .hdrsize = 0, | ||
2332 | .name = IPVS_GENL_NAME, | ||
2333 | .version = IPVS_GENL_VERSION, | ||
2334 | .maxattr = IPVS_CMD_MAX, | ||
2335 | }; | ||
2336 | |||
2337 | /* Policy used for first-level command attributes */ | ||
2338 | static const struct nla_policy ip_vs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = { | ||
2339 | [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED }, | ||
2340 | [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED }, | ||
2341 | [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED }, | ||
2342 | [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 }, | ||
2343 | [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 }, | ||
2344 | [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 }, | ||
2345 | }; | ||
2346 | |||
2347 | /* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DAEMON */ | ||
2348 | static const struct nla_policy ip_vs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = { | ||
2349 | [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 }, | ||
2350 | [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_NUL_STRING, | ||
2351 | .len = IP_VS_IFNAME_MAXLEN }, | ||
2352 | [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 }, | ||
2353 | }; | ||
2354 | |||
2355 | /* Policy used for attributes in nested attribute IPVS_CMD_ATTR_SERVICE */ | ||
2356 | static const struct nla_policy ip_vs_svc_policy[IPVS_SVC_ATTR_MAX + 1] = { | ||
2357 | [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 }, | ||
2358 | [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 }, | ||
2359 | [IPVS_SVC_ATTR_ADDR] = { .type = NLA_BINARY, | ||
2360 | .len = sizeof(union nf_inet_addr) }, | ||
2361 | [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 }, | ||
2362 | [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 }, | ||
2363 | [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_NUL_STRING, | ||
2364 | .len = IP_VS_SCHEDNAME_MAXLEN }, | ||
2365 | [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_BINARY, | ||
2366 | .len = sizeof(struct ip_vs_flags) }, | ||
2367 | [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 }, | ||
2368 | [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 }, | ||
2369 | [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED }, | ||
2370 | }; | ||
2371 | |||
2372 | /* Policy used for attributes in nested attribute IPVS_CMD_ATTR_DEST */ | ||
2373 | static const struct nla_policy ip_vs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = { | ||
2374 | [IPVS_DEST_ATTR_ADDR] = { .type = NLA_BINARY, | ||
2375 | .len = sizeof(union nf_inet_addr) }, | ||
2376 | [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 }, | ||
2377 | [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 }, | ||
2378 | [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 }, | ||
2379 | [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 }, | ||
2380 | [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 }, | ||
2381 | [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 }, | ||
2382 | [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 }, | ||
2383 | [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 }, | ||
2384 | [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED }, | ||
2385 | }; | ||
2386 | |||
2387 | static int ip_vs_genl_fill_stats(struct sk_buff *skb, int container_type, | ||
2388 | struct ip_vs_stats *stats) | ||
2389 | { | ||
2390 | struct nlattr *nl_stats = nla_nest_start(skb, container_type); | ||
2391 | if (!nl_stats) | ||
2392 | return -EMSGSIZE; | ||
2393 | |||
2394 | spin_lock_bh(&stats->lock); | ||
2395 | |||
2396 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CONNS, stats->conns); | ||
2397 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPKTS, stats->inpkts); | ||
2398 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPKTS, stats->outpkts); | ||
2399 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_INBYTES, stats->inbytes); | ||
2400 | NLA_PUT_U64(skb, IPVS_STATS_ATTR_OUTBYTES, stats->outbytes); | ||
2401 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_CPS, stats->cps); | ||
2402 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INPPS, stats->inpps); | ||
2403 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTPPS, stats->outpps); | ||
2404 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_INBPS, stats->inbps); | ||
2405 | NLA_PUT_U32(skb, IPVS_STATS_ATTR_OUTBPS, stats->outbps); | ||
2406 | |||
2407 | spin_unlock_bh(&stats->lock); | ||
2408 | |||
2409 | nla_nest_end(skb, nl_stats); | ||
2410 | |||
2411 | return 0; | ||
2412 | |||
2413 | nla_put_failure: | ||
2414 | spin_unlock_bh(&stats->lock); | ||
2415 | nla_nest_cancel(skb, nl_stats); | ||
2416 | return -EMSGSIZE; | ||
2417 | } | ||
2418 | |||
2419 | static int ip_vs_genl_fill_service(struct sk_buff *skb, | ||
2420 | struct ip_vs_service *svc) | ||
2421 | { | ||
2422 | struct nlattr *nl_service; | ||
2423 | struct ip_vs_flags flags = { .flags = svc->flags, | ||
2424 | .mask = ~0 }; | ||
2425 | |||
2426 | nl_service = nla_nest_start(skb, IPVS_CMD_ATTR_SERVICE); | ||
2427 | if (!nl_service) | ||
2428 | return -EMSGSIZE; | ||
2429 | |||
2430 | NLA_PUT_U16(skb, IPVS_SVC_ATTR_AF, AF_INET); | ||
2431 | |||
2432 | if (svc->fwmark) { | ||
2433 | NLA_PUT_U32(skb, IPVS_SVC_ATTR_FWMARK, svc->fwmark); | ||
2434 | } else { | ||
2435 | NLA_PUT_U16(skb, IPVS_SVC_ATTR_PROTOCOL, svc->protocol); | ||
2436 | NLA_PUT(skb, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &svc->addr); | ||
2437 | NLA_PUT_U16(skb, IPVS_SVC_ATTR_PORT, svc->port); | ||
2438 | } | ||
2439 | |||
2440 | NLA_PUT_STRING(skb, IPVS_SVC_ATTR_SCHED_NAME, svc->scheduler->name); | ||
2441 | NLA_PUT(skb, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags); | ||
2442 | NLA_PUT_U32(skb, IPVS_SVC_ATTR_TIMEOUT, svc->timeout / HZ); | ||
2443 | NLA_PUT_U32(skb, IPVS_SVC_ATTR_NETMASK, svc->netmask); | ||
2444 | |||
2445 | if (ip_vs_genl_fill_stats(skb, IPVS_SVC_ATTR_STATS, &svc->stats)) | ||
2446 | goto nla_put_failure; | ||
2447 | |||
2448 | nla_nest_end(skb, nl_service); | ||
2449 | |||
2450 | return 0; | ||
2451 | |||
2452 | nla_put_failure: | ||
2453 | nla_nest_cancel(skb, nl_service); | ||
2454 | return -EMSGSIZE; | ||
2455 | } | ||
2456 | |||
2457 | static int ip_vs_genl_dump_service(struct sk_buff *skb, | ||
2458 | struct ip_vs_service *svc, | ||
2459 | struct netlink_callback *cb) | ||
2460 | { | ||
2461 | void *hdr; | ||
2462 | |||
2463 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, | ||
2464 | &ip_vs_genl_family, NLM_F_MULTI, | ||
2465 | IPVS_CMD_NEW_SERVICE); | ||
2466 | if (!hdr) | ||
2467 | return -EMSGSIZE; | ||
2468 | |||
2469 | if (ip_vs_genl_fill_service(skb, svc) < 0) | ||
2470 | goto nla_put_failure; | ||
2471 | |||
2472 | return genlmsg_end(skb, hdr); | ||
2473 | |||
2474 | nla_put_failure: | ||
2475 | genlmsg_cancel(skb, hdr); | ||
2476 | return -EMSGSIZE; | ||
2477 | } | ||
2478 | |||
2479 | static int ip_vs_genl_dump_services(struct sk_buff *skb, | ||
2480 | struct netlink_callback *cb) | ||
2481 | { | ||
2482 | int idx = 0, i; | ||
2483 | int start = cb->args[0]; | ||
2484 | struct ip_vs_service *svc; | ||
2485 | |||
2486 | mutex_lock(&__ip_vs_mutex); | ||
2487 | for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { | ||
2488 | list_for_each_entry(svc, &ip_vs_svc_table[i], s_list) { | ||
2489 | if (++idx <= start) | ||
2490 | continue; | ||
2491 | if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { | ||
2492 | idx--; | ||
2493 | goto nla_put_failure; | ||
2494 | } | ||
2495 | } | ||
2496 | } | ||
2497 | |||
2498 | for (i = 0; i < IP_VS_SVC_TAB_SIZE; i++) { | ||
2499 | list_for_each_entry(svc, &ip_vs_svc_fwm_table[i], f_list) { | ||
2500 | if (++idx <= start) | ||
2501 | continue; | ||
2502 | if (ip_vs_genl_dump_service(skb, svc, cb) < 0) { | ||
2503 | idx--; | ||
2504 | goto nla_put_failure; | ||
2505 | } | ||
2506 | } | ||
2507 | } | ||
2508 | |||
2509 | nla_put_failure: | ||
2510 | mutex_unlock(&__ip_vs_mutex); | ||
2511 | cb->args[0] = idx; | ||
2512 | |||
2513 | return skb->len; | ||
2514 | } | ||
2515 | |||
2516 | static int ip_vs_genl_parse_service(struct ip_vs_service_user *usvc, | ||
2517 | struct nlattr *nla, int full_entry) | ||
2518 | { | ||
2519 | struct nlattr *attrs[IPVS_SVC_ATTR_MAX + 1]; | ||
2520 | struct nlattr *nla_af, *nla_port, *nla_fwmark, *nla_protocol, *nla_addr; | ||
2521 | |||
2522 | /* Parse mandatory identifying service fields first */ | ||
2523 | if (nla == NULL || | ||
2524 | nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy)) | ||
2525 | return -EINVAL; | ||
2526 | |||
2527 | nla_af = attrs[IPVS_SVC_ATTR_AF]; | ||
2528 | nla_protocol = attrs[IPVS_SVC_ATTR_PROTOCOL]; | ||
2529 | nla_addr = attrs[IPVS_SVC_ATTR_ADDR]; | ||
2530 | nla_port = attrs[IPVS_SVC_ATTR_PORT]; | ||
2531 | nla_fwmark = attrs[IPVS_SVC_ATTR_FWMARK]; | ||
2532 | |||
2533 | if (!(nla_af && (nla_fwmark || (nla_port && nla_protocol && nla_addr)))) | ||
2534 | return -EINVAL; | ||
2535 | |||
2536 | /* For now, only support IPv4 */ | ||
2537 | if (nla_get_u16(nla_af) != AF_INET) | ||
2538 | return -EAFNOSUPPORT; | ||
2539 | |||
2540 | if (nla_fwmark) { | ||
2541 | usvc->protocol = IPPROTO_TCP; | ||
2542 | usvc->fwmark = nla_get_u32(nla_fwmark); | ||
2543 | } else { | ||
2544 | usvc->protocol = nla_get_u16(nla_protocol); | ||
2545 | nla_memcpy(&usvc->addr, nla_addr, sizeof(usvc->addr)); | ||
2546 | usvc->port = nla_get_u16(nla_port); | ||
2547 | usvc->fwmark = 0; | ||
2548 | } | ||
2549 | |||
2550 | /* If a full entry was requested, check for the additional fields */ | ||
2551 | if (full_entry) { | ||
2552 | struct nlattr *nla_sched, *nla_flags, *nla_timeout, | ||
2553 | *nla_netmask; | ||
2554 | struct ip_vs_flags flags; | ||
2555 | struct ip_vs_service *svc; | ||
2556 | |||
2557 | nla_sched = attrs[IPVS_SVC_ATTR_SCHED_NAME]; | ||
2558 | nla_flags = attrs[IPVS_SVC_ATTR_FLAGS]; | ||
2559 | nla_timeout = attrs[IPVS_SVC_ATTR_TIMEOUT]; | ||
2560 | nla_netmask = attrs[IPVS_SVC_ATTR_NETMASK]; | ||
2561 | |||
2562 | if (!(nla_sched && nla_flags && nla_timeout && nla_netmask)) | ||
2563 | return -EINVAL; | ||
2564 | |||
2565 | nla_memcpy(&flags, nla_flags, sizeof(flags)); | ||
2566 | |||
2567 | /* prefill flags from service if it already exists */ | ||
2568 | if (usvc->fwmark) | ||
2569 | svc = __ip_vs_svc_fwm_get(usvc->fwmark); | ||
2570 | else | ||
2571 | svc = __ip_vs_service_get(usvc->protocol, usvc->addr, | ||
2572 | usvc->port); | ||
2573 | if (svc) { | ||
2574 | usvc->flags = svc->flags; | ||
2575 | ip_vs_service_put(svc); | ||
2576 | } else | ||
2577 | usvc->flags = 0; | ||
2578 | |||
2579 | /* set new flags from userland */ | ||
2580 | usvc->flags = (usvc->flags & ~flags.mask) | | ||
2581 | (flags.flags & flags.mask); | ||
2582 | |||
2583 | strlcpy(usvc->sched_name, nla_data(nla_sched), | ||
2584 | sizeof(usvc->sched_name)); | ||
2585 | usvc->timeout = nla_get_u32(nla_timeout); | ||
2586 | usvc->netmask = nla_get_u32(nla_netmask); | ||
2587 | } | ||
2588 | |||
2589 | return 0; | ||
2590 | } | ||
2591 | |||
2592 | static struct ip_vs_service *ip_vs_genl_find_service(struct nlattr *nla) | ||
2593 | { | ||
2594 | struct ip_vs_service_user usvc; | ||
2595 | int ret; | ||
2596 | |||
2597 | ret = ip_vs_genl_parse_service(&usvc, nla, 0); | ||
2598 | if (ret) | ||
2599 | return ERR_PTR(ret); | ||
2600 | |||
2601 | if (usvc.fwmark) | ||
2602 | return __ip_vs_svc_fwm_get(usvc.fwmark); | ||
2603 | else | ||
2604 | return __ip_vs_service_get(usvc.protocol, usvc.addr, | ||
2605 | usvc.port); | ||
2606 | } | ||
2607 | |||
2608 | static int ip_vs_genl_fill_dest(struct sk_buff *skb, struct ip_vs_dest *dest) | ||
2609 | { | ||
2610 | struct nlattr *nl_dest; | ||
2611 | |||
2612 | nl_dest = nla_nest_start(skb, IPVS_CMD_ATTR_DEST); | ||
2613 | if (!nl_dest) | ||
2614 | return -EMSGSIZE; | ||
2615 | |||
2616 | NLA_PUT(skb, IPVS_DEST_ATTR_ADDR, sizeof(dest->addr), &dest->addr); | ||
2617 | NLA_PUT_U16(skb, IPVS_DEST_ATTR_PORT, dest->port); | ||
2618 | |||
2619 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_FWD_METHOD, | ||
2620 | atomic_read(&dest->conn_flags) & IP_VS_CONN_F_FWD_MASK); | ||
2621 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_WEIGHT, atomic_read(&dest->weight)); | ||
2622 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_U_THRESH, dest->u_threshold); | ||
2623 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_L_THRESH, dest->l_threshold); | ||
2624 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_ACTIVE_CONNS, | ||
2625 | atomic_read(&dest->activeconns)); | ||
2626 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_INACT_CONNS, | ||
2627 | atomic_read(&dest->inactconns)); | ||
2628 | NLA_PUT_U32(skb, IPVS_DEST_ATTR_PERSIST_CONNS, | ||
2629 | atomic_read(&dest->persistconns)); | ||
2630 | |||
2631 | if (ip_vs_genl_fill_stats(skb, IPVS_DEST_ATTR_STATS, &dest->stats)) | ||
2632 | goto nla_put_failure; | ||
2633 | |||
2634 | nla_nest_end(skb, nl_dest); | ||
2635 | |||
2636 | return 0; | ||
2637 | |||
2638 | nla_put_failure: | ||
2639 | nla_nest_cancel(skb, nl_dest); | ||
2640 | return -EMSGSIZE; | ||
2641 | } | ||
2642 | |||
2643 | static int ip_vs_genl_dump_dest(struct sk_buff *skb, struct ip_vs_dest *dest, | ||
2644 | struct netlink_callback *cb) | ||
2645 | { | ||
2646 | void *hdr; | ||
2647 | |||
2648 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, | ||
2649 | &ip_vs_genl_family, NLM_F_MULTI, | ||
2650 | IPVS_CMD_NEW_DEST); | ||
2651 | if (!hdr) | ||
2652 | return -EMSGSIZE; | ||
2653 | |||
2654 | if (ip_vs_genl_fill_dest(skb, dest) < 0) | ||
2655 | goto nla_put_failure; | ||
2656 | |||
2657 | return genlmsg_end(skb, hdr); | ||
2658 | |||
2659 | nla_put_failure: | ||
2660 | genlmsg_cancel(skb, hdr); | ||
2661 | return -EMSGSIZE; | ||
2662 | } | ||
2663 | |||
2664 | static int ip_vs_genl_dump_dests(struct sk_buff *skb, | ||
2665 | struct netlink_callback *cb) | ||
2666 | { | ||
2667 | int idx = 0; | ||
2668 | int start = cb->args[0]; | ||
2669 | struct ip_vs_service *svc; | ||
2670 | struct ip_vs_dest *dest; | ||
2671 | struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; | ||
2672 | |||
2673 | mutex_lock(&__ip_vs_mutex); | ||
2674 | |||
2675 | /* Try to find the service for which to dump destinations */ | ||
2676 | if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, | ||
2677 | IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy)) | ||
2678 | goto out_err; | ||
2679 | |||
2680 | svc = ip_vs_genl_find_service(attrs[IPVS_CMD_ATTR_SERVICE]); | ||
2681 | if (IS_ERR(svc) || svc == NULL) | ||
2682 | goto out_err; | ||
2683 | |||
2684 | /* Dump the destinations */ | ||
2685 | list_for_each_entry(dest, &svc->destinations, n_list) { | ||
2686 | if (++idx <= start) | ||
2687 | continue; | ||
2688 | if (ip_vs_genl_dump_dest(skb, dest, cb) < 0) { | ||
2689 | idx--; | ||
2690 | goto nla_put_failure; | ||
2691 | } | ||
2692 | } | ||
2693 | |||
2694 | nla_put_failure: | ||
2695 | cb->args[0] = idx; | ||
2696 | ip_vs_service_put(svc); | ||
2697 | |||
2698 | out_err: | ||
2699 | mutex_unlock(&__ip_vs_mutex); | ||
2700 | |||
2701 | return skb->len; | ||
2702 | } | ||
2703 | |||
2704 | static int ip_vs_genl_parse_dest(struct ip_vs_dest_user *udest, | ||
2705 | struct nlattr *nla, int full_entry) | ||
2706 | { | ||
2707 | struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1]; | ||
2708 | struct nlattr *nla_addr, *nla_port; | ||
2709 | |||
2710 | /* Parse mandatory identifying destination fields first */ | ||
2711 | if (nla == NULL || | ||
2712 | nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy)) | ||
2713 | return -EINVAL; | ||
2714 | |||
2715 | nla_addr = attrs[IPVS_DEST_ATTR_ADDR]; | ||
2716 | nla_port = attrs[IPVS_DEST_ATTR_PORT]; | ||
2717 | |||
2718 | if (!(nla_addr && nla_port)) | ||
2719 | return -EINVAL; | ||
2720 | |||
2721 | nla_memcpy(&udest->addr, nla_addr, sizeof(udest->addr)); | ||
2722 | udest->port = nla_get_u16(nla_port); | ||
2723 | |||
2724 | /* If a full entry was requested, check for the additional fields */ | ||
2725 | if (full_entry) { | ||
2726 | struct nlattr *nla_fwd, *nla_weight, *nla_u_thresh, | ||
2727 | *nla_l_thresh; | ||
2728 | |||
2729 | nla_fwd = attrs[IPVS_DEST_ATTR_FWD_METHOD]; | ||
2730 | nla_weight = attrs[IPVS_DEST_ATTR_WEIGHT]; | ||
2731 | nla_u_thresh = attrs[IPVS_DEST_ATTR_U_THRESH]; | ||
2732 | nla_l_thresh = attrs[IPVS_DEST_ATTR_L_THRESH]; | ||
2733 | |||
2734 | if (!(nla_fwd && nla_weight && nla_u_thresh && nla_l_thresh)) | ||
2735 | return -EINVAL; | ||
2736 | |||
2737 | udest->conn_flags = nla_get_u32(nla_fwd) | ||
2738 | & IP_VS_CONN_F_FWD_MASK; | ||
2739 | udest->weight = nla_get_u32(nla_weight); | ||
2740 | udest->u_threshold = nla_get_u32(nla_u_thresh); | ||
2741 | udest->l_threshold = nla_get_u32(nla_l_thresh); | ||
2742 | } | ||
2743 | |||
2744 | return 0; | ||
2745 | } | ||
2746 | |||
2747 | static int ip_vs_genl_fill_daemon(struct sk_buff *skb, __be32 state, | ||
2748 | const char *mcast_ifn, __be32 syncid) | ||
2749 | { | ||
2750 | struct nlattr *nl_daemon; | ||
2751 | |||
2752 | nl_daemon = nla_nest_start(skb, IPVS_CMD_ATTR_DAEMON); | ||
2753 | if (!nl_daemon) | ||
2754 | return -EMSGSIZE; | ||
2755 | |||
2756 | NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_STATE, state); | ||
2757 | NLA_PUT_STRING(skb, IPVS_DAEMON_ATTR_MCAST_IFN, mcast_ifn); | ||
2758 | NLA_PUT_U32(skb, IPVS_DAEMON_ATTR_SYNC_ID, syncid); | ||
2759 | |||
2760 | nla_nest_end(skb, nl_daemon); | ||
2761 | |||
2762 | return 0; | ||
2763 | |||
2764 | nla_put_failure: | ||
2765 | nla_nest_cancel(skb, nl_daemon); | ||
2766 | return -EMSGSIZE; | ||
2767 | } | ||
2768 | |||
2769 | static int ip_vs_genl_dump_daemon(struct sk_buff *skb, __be32 state, | ||
2770 | const char *mcast_ifn, __be32 syncid, | ||
2771 | struct netlink_callback *cb) | ||
2772 | { | ||
2773 | void *hdr; | ||
2774 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, | ||
2775 | &ip_vs_genl_family, NLM_F_MULTI, | ||
2776 | IPVS_CMD_NEW_DAEMON); | ||
2777 | if (!hdr) | ||
2778 | return -EMSGSIZE; | ||
2779 | |||
2780 | if (ip_vs_genl_fill_daemon(skb, state, mcast_ifn, syncid)) | ||
2781 | goto nla_put_failure; | ||
2782 | |||
2783 | return genlmsg_end(skb, hdr); | ||
2784 | |||
2785 | nla_put_failure: | ||
2786 | genlmsg_cancel(skb, hdr); | ||
2787 | return -EMSGSIZE; | ||
2788 | } | ||
2789 | |||
2790 | static int ip_vs_genl_dump_daemons(struct sk_buff *skb, | ||
2791 | struct netlink_callback *cb) | ||
2792 | { | ||
2793 | mutex_lock(&__ip_vs_mutex); | ||
2794 | if ((ip_vs_sync_state & IP_VS_STATE_MASTER) && !cb->args[0]) { | ||
2795 | if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_MASTER, | ||
2796 | ip_vs_master_mcast_ifn, | ||
2797 | ip_vs_master_syncid, cb) < 0) | ||
2798 | goto nla_put_failure; | ||
2799 | |||
2800 | cb->args[0] = 1; | ||
2801 | } | ||
2802 | |||
2803 | if ((ip_vs_sync_state & IP_VS_STATE_BACKUP) && !cb->args[1]) { | ||
2804 | if (ip_vs_genl_dump_daemon(skb, IP_VS_STATE_BACKUP, | ||
2805 | ip_vs_backup_mcast_ifn, | ||
2806 | ip_vs_backup_syncid, cb) < 0) | ||
2807 | goto nla_put_failure; | ||
2808 | |||
2809 | cb->args[1] = 1; | ||
2810 | } | ||
2811 | |||
2812 | nla_put_failure: | ||
2813 | mutex_unlock(&__ip_vs_mutex); | ||
2814 | |||
2815 | return skb->len; | ||
2816 | } | ||
2817 | |||
2818 | static int ip_vs_genl_new_daemon(struct nlattr **attrs) | ||
2819 | { | ||
2820 | if (!(attrs[IPVS_DAEMON_ATTR_STATE] && | ||
2821 | attrs[IPVS_DAEMON_ATTR_MCAST_IFN] && | ||
2822 | attrs[IPVS_DAEMON_ATTR_SYNC_ID])) | ||
2823 | return -EINVAL; | ||
2824 | |||
2825 | return start_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]), | ||
2826 | nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]), | ||
2827 | nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID])); | ||
2828 | } | ||
2829 | |||
2830 | static int ip_vs_genl_del_daemon(struct nlattr **attrs) | ||
2831 | { | ||
2832 | if (!attrs[IPVS_DAEMON_ATTR_STATE]) | ||
2833 | return -EINVAL; | ||
2834 | |||
2835 | return stop_sync_thread(nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE])); | ||
2836 | } | ||
2837 | |||
2838 | static int ip_vs_genl_set_config(struct nlattr **attrs) | ||
2839 | { | ||
2840 | struct ip_vs_timeout_user t; | ||
2841 | |||
2842 | __ip_vs_get_timeouts(&t); | ||
2843 | |||
2844 | if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]) | ||
2845 | t.tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]); | ||
2846 | |||
2847 | if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]) | ||
2848 | t.tcp_fin_timeout = | ||
2849 | nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]); | ||
2850 | |||
2851 | if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]) | ||
2852 | t.udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]); | ||
2853 | |||
2854 | return ip_vs_set_timeout(&t); | ||
2855 | } | ||
2856 | |||
2857 | static int ip_vs_genl_set_cmd(struct sk_buff *skb, struct genl_info *info) | ||
2858 | { | ||
2859 | struct ip_vs_service *svc = NULL; | ||
2860 | struct ip_vs_service_user usvc; | ||
2861 | struct ip_vs_dest_user udest; | ||
2862 | int ret = 0, cmd; | ||
2863 | int need_full_svc = 0, need_full_dest = 0; | ||
2864 | |||
2865 | cmd = info->genlhdr->cmd; | ||
2866 | |||
2867 | mutex_lock(&__ip_vs_mutex); | ||
2868 | |||
2869 | if (cmd == IPVS_CMD_FLUSH) { | ||
2870 | ret = ip_vs_flush(); | ||
2871 | goto out; | ||
2872 | } else if (cmd == IPVS_CMD_SET_CONFIG) { | ||
2873 | ret = ip_vs_genl_set_config(info->attrs); | ||
2874 | goto out; | ||
2875 | } else if (cmd == IPVS_CMD_NEW_DAEMON || | ||
2876 | cmd == IPVS_CMD_DEL_DAEMON) { | ||
2877 | |||
2878 | struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1]; | ||
2879 | |||
2880 | if (!info->attrs[IPVS_CMD_ATTR_DAEMON] || | ||
2881 | nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX, | ||
2882 | info->attrs[IPVS_CMD_ATTR_DAEMON], | ||
2883 | ip_vs_daemon_policy)) { | ||
2884 | ret = -EINVAL; | ||
2885 | goto out; | ||
2886 | } | ||
2887 | |||
2888 | if (cmd == IPVS_CMD_NEW_DAEMON) | ||
2889 | ret = ip_vs_genl_new_daemon(daemon_attrs); | ||
2890 | else | ||
2891 | ret = ip_vs_genl_del_daemon(daemon_attrs); | ||
2892 | goto out; | ||
2893 | } else if (cmd == IPVS_CMD_ZERO && | ||
2894 | !info->attrs[IPVS_CMD_ATTR_SERVICE]) { | ||
2895 | ret = ip_vs_zero_all(); | ||
2896 | goto out; | ||
2897 | } | ||
2898 | |||
2899 | /* All following commands require a service argument, so check if we | ||
2900 | * received a valid one. We need a full service specification when | ||
2901 | * adding / editing a service. Only identifying members otherwise. */ | ||
2902 | if (cmd == IPVS_CMD_NEW_SERVICE || cmd == IPVS_CMD_SET_SERVICE) | ||
2903 | need_full_svc = 1; | ||
2904 | |||
2905 | ret = ip_vs_genl_parse_service(&usvc, | ||
2906 | info->attrs[IPVS_CMD_ATTR_SERVICE], | ||
2907 | need_full_svc); | ||
2908 | if (ret) | ||
2909 | goto out; | ||
2910 | |||
2911 | /* Lookup the exact service by <protocol, addr, port> or fwmark */ | ||
2912 | if (usvc.fwmark == 0) | ||
2913 | svc = __ip_vs_service_get(usvc.protocol, usvc.addr, usvc.port); | ||
2914 | else | ||
2915 | svc = __ip_vs_svc_fwm_get(usvc.fwmark); | ||
2916 | |||
2917 | /* Unless we're adding a new service, the service must already exist */ | ||
2918 | if ((cmd != IPVS_CMD_NEW_SERVICE) && (svc == NULL)) { | ||
2919 | ret = -ESRCH; | ||
2920 | goto out; | ||
2921 | } | ||
2922 | |||
2923 | /* Destination commands require a valid destination argument. For | ||
2924 | * adding / editing a destination, we need a full destination | ||
2925 | * specification. */ | ||
2926 | if (cmd == IPVS_CMD_NEW_DEST || cmd == IPVS_CMD_SET_DEST || | ||
2927 | cmd == IPVS_CMD_DEL_DEST) { | ||
2928 | if (cmd != IPVS_CMD_DEL_DEST) | ||
2929 | need_full_dest = 1; | ||
2930 | |||
2931 | ret = ip_vs_genl_parse_dest(&udest, | ||
2932 | info->attrs[IPVS_CMD_ATTR_DEST], | ||
2933 | need_full_dest); | ||
2934 | if (ret) | ||
2935 | goto out; | ||
2936 | } | ||
2937 | |||
2938 | switch (cmd) { | ||
2939 | case IPVS_CMD_NEW_SERVICE: | ||
2940 | if (svc == NULL) | ||
2941 | ret = ip_vs_add_service(&usvc, &svc); | ||
2942 | else | ||
2943 | ret = -EEXIST; | ||
2944 | break; | ||
2945 | case IPVS_CMD_SET_SERVICE: | ||
2946 | ret = ip_vs_edit_service(svc, &usvc); | ||
2947 | break; | ||
2948 | case IPVS_CMD_DEL_SERVICE: | ||
2949 | ret = ip_vs_del_service(svc); | ||
2950 | break; | ||
2951 | case IPVS_CMD_NEW_DEST: | ||
2952 | ret = ip_vs_add_dest(svc, &udest); | ||
2953 | break; | ||
2954 | case IPVS_CMD_SET_DEST: | ||
2955 | ret = ip_vs_edit_dest(svc, &udest); | ||
2956 | break; | ||
2957 | case IPVS_CMD_DEL_DEST: | ||
2958 | ret = ip_vs_del_dest(svc, &udest); | ||
2959 | break; | ||
2960 | case IPVS_CMD_ZERO: | ||
2961 | ret = ip_vs_zero_service(svc); | ||
2962 | break; | ||
2963 | default: | ||
2964 | ret = -EINVAL; | ||
2965 | } | ||
2966 | |||
2967 | out: | ||
2968 | if (svc) | ||
2969 | ip_vs_service_put(svc); | ||
2970 | mutex_unlock(&__ip_vs_mutex); | ||
2971 | |||
2972 | return ret; | ||
2973 | } | ||
2974 | |||
2975 | static int ip_vs_genl_get_cmd(struct sk_buff *skb, struct genl_info *info) | ||
2976 | { | ||
2977 | struct sk_buff *msg; | ||
2978 | void *reply; | ||
2979 | int ret, cmd, reply_cmd; | ||
2980 | |||
2981 | cmd = info->genlhdr->cmd; | ||
2982 | |||
2983 | if (cmd == IPVS_CMD_GET_SERVICE) | ||
2984 | reply_cmd = IPVS_CMD_NEW_SERVICE; | ||
2985 | else if (cmd == IPVS_CMD_GET_INFO) | ||
2986 | reply_cmd = IPVS_CMD_SET_INFO; | ||
2987 | else if (cmd == IPVS_CMD_GET_CONFIG) | ||
2988 | reply_cmd = IPVS_CMD_SET_CONFIG; | ||
2989 | else { | ||
2990 | IP_VS_ERR("unknown Generic Netlink command\n"); | ||
2991 | return -EINVAL; | ||
2992 | } | ||
2993 | |||
2994 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
2995 | if (!msg) | ||
2996 | return -ENOMEM; | ||
2997 | |||
2998 | mutex_lock(&__ip_vs_mutex); | ||
2999 | |||
3000 | reply = genlmsg_put_reply(msg, info, &ip_vs_genl_family, 0, reply_cmd); | ||
3001 | if (reply == NULL) | ||
3002 | goto nla_put_failure; | ||
3003 | |||
3004 | switch (cmd) { | ||
3005 | case IPVS_CMD_GET_SERVICE: | ||
3006 | { | ||
3007 | struct ip_vs_service *svc; | ||
3008 | |||
3009 | svc = ip_vs_genl_find_service(info->attrs[IPVS_CMD_ATTR_SERVICE]); | ||
3010 | if (IS_ERR(svc)) { | ||
3011 | ret = PTR_ERR(svc); | ||
3012 | goto out_err; | ||
3013 | } else if (svc) { | ||
3014 | ret = ip_vs_genl_fill_service(msg, svc); | ||
3015 | ip_vs_service_put(svc); | ||
3016 | if (ret) | ||
3017 | goto nla_put_failure; | ||
3018 | } else { | ||
3019 | ret = -ESRCH; | ||
3020 | goto out_err; | ||
3021 | } | ||
3022 | |||
3023 | break; | ||
3024 | } | ||
3025 | |||
3026 | case IPVS_CMD_GET_CONFIG: | ||
3027 | { | ||
3028 | struct ip_vs_timeout_user t; | ||
3029 | |||
3030 | __ip_vs_get_timeouts(&t); | ||
3031 | #ifdef CONFIG_IP_VS_PROTO_TCP | ||
3032 | NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, t.tcp_timeout); | ||
3033 | NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, | ||
3034 | t.tcp_fin_timeout); | ||
3035 | #endif | ||
3036 | #ifdef CONFIG_IP_VS_PROTO_UDP | ||
3037 | NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, t.udp_timeout); | ||
3038 | #endif | ||
3039 | |||
3040 | break; | ||
3041 | } | ||
3042 | |||
3043 | case IPVS_CMD_GET_INFO: | ||
3044 | NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE); | ||
3045 | NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE, | ||
3046 | IP_VS_CONN_TAB_SIZE); | ||
3047 | break; | ||
3048 | } | ||
3049 | |||
3050 | genlmsg_end(msg, reply); | ||
3051 | ret = genlmsg_unicast(msg, info->snd_pid); | ||
3052 | goto out; | ||
3053 | |||
3054 | nla_put_failure: | ||
3055 | IP_VS_ERR("not enough space in Netlink message\n"); | ||
3056 | ret = -EMSGSIZE; | ||
3057 | |||
3058 | out_err: | ||
3059 | nlmsg_free(msg); | ||
3060 | out: | ||
3061 | mutex_unlock(&__ip_vs_mutex); | ||
3062 | |||
3063 | return ret; | ||
3064 | } | ||
3065 | |||
3066 | |||
3067 | static struct genl_ops ip_vs_genl_ops[] __read_mostly = { | ||
3068 | { | ||
3069 | .cmd = IPVS_CMD_NEW_SERVICE, | ||
3070 | .flags = GENL_ADMIN_PERM, | ||
3071 | .policy = ip_vs_cmd_policy, | ||
3072 | .doit = ip_vs_genl_set_cmd, | ||
3073 | }, | ||
3074 | { | ||
3075 | .cmd = IPVS_CMD_SET_SERVICE, | ||
3076 | .flags = GENL_ADMIN_PERM, | ||
3077 | .policy = ip_vs_cmd_policy, | ||
3078 | .doit = ip_vs_genl_set_cmd, | ||
3079 | }, | ||
3080 | { | ||
3081 | .cmd = IPVS_CMD_DEL_SERVICE, | ||
3082 | .flags = GENL_ADMIN_PERM, | ||
3083 | .policy = ip_vs_cmd_policy, | ||
3084 | .doit = ip_vs_genl_set_cmd, | ||
3085 | }, | ||
3086 | { | ||
3087 | .cmd = IPVS_CMD_GET_SERVICE, | ||
3088 | .flags = GENL_ADMIN_PERM, | ||
3089 | .doit = ip_vs_genl_get_cmd, | ||
3090 | .dumpit = ip_vs_genl_dump_services, | ||
3091 | .policy = ip_vs_cmd_policy, | ||
3092 | }, | ||
3093 | { | ||
3094 | .cmd = IPVS_CMD_NEW_DEST, | ||
3095 | .flags = GENL_ADMIN_PERM, | ||
3096 | .policy = ip_vs_cmd_policy, | ||
3097 | .doit = ip_vs_genl_set_cmd, | ||
3098 | }, | ||
3099 | { | ||
3100 | .cmd = IPVS_CMD_SET_DEST, | ||
3101 | .flags = GENL_ADMIN_PERM, | ||
3102 | .policy = ip_vs_cmd_policy, | ||
3103 | .doit = ip_vs_genl_set_cmd, | ||
3104 | }, | ||
3105 | { | ||
3106 | .cmd = IPVS_CMD_DEL_DEST, | ||
3107 | .flags = GENL_ADMIN_PERM, | ||
3108 | .policy = ip_vs_cmd_policy, | ||
3109 | .doit = ip_vs_genl_set_cmd, | ||
3110 | }, | ||
3111 | { | ||
3112 | .cmd = IPVS_CMD_GET_DEST, | ||
3113 | .flags = GENL_ADMIN_PERM, | ||
3114 | .policy = ip_vs_cmd_policy, | ||
3115 | .dumpit = ip_vs_genl_dump_dests, | ||
3116 | }, | ||
3117 | { | ||
3118 | .cmd = IPVS_CMD_NEW_DAEMON, | ||
3119 | .flags = GENL_ADMIN_PERM, | ||
3120 | .policy = ip_vs_cmd_policy, | ||
3121 | .doit = ip_vs_genl_set_cmd, | ||
3122 | }, | ||
3123 | { | ||
3124 | .cmd = IPVS_CMD_DEL_DAEMON, | ||
3125 | .flags = GENL_ADMIN_PERM, | ||
3126 | .policy = ip_vs_cmd_policy, | ||
3127 | .doit = ip_vs_genl_set_cmd, | ||
3128 | }, | ||
3129 | { | ||
3130 | .cmd = IPVS_CMD_GET_DAEMON, | ||
3131 | .flags = GENL_ADMIN_PERM, | ||
3132 | .dumpit = ip_vs_genl_dump_daemons, | ||
3133 | }, | ||
3134 | { | ||
3135 | .cmd = IPVS_CMD_SET_CONFIG, | ||
3136 | .flags = GENL_ADMIN_PERM, | ||
3137 | .policy = ip_vs_cmd_policy, | ||
3138 | .doit = ip_vs_genl_set_cmd, | ||
3139 | }, | ||
3140 | { | ||
3141 | .cmd = IPVS_CMD_GET_CONFIG, | ||
3142 | .flags = GENL_ADMIN_PERM, | ||
3143 | .doit = ip_vs_genl_get_cmd, | ||
3144 | }, | ||
3145 | { | ||
3146 | .cmd = IPVS_CMD_GET_INFO, | ||
3147 | .flags = GENL_ADMIN_PERM, | ||
3148 | .doit = ip_vs_genl_get_cmd, | ||
3149 | }, | ||
3150 | { | ||
3151 | .cmd = IPVS_CMD_ZERO, | ||
3152 | .flags = GENL_ADMIN_PERM, | ||
3153 | .policy = ip_vs_cmd_policy, | ||
3154 | .doit = ip_vs_genl_set_cmd, | ||
3155 | }, | ||
3156 | { | ||
3157 | .cmd = IPVS_CMD_FLUSH, | ||
3158 | .flags = GENL_ADMIN_PERM, | ||
3159 | .doit = ip_vs_genl_set_cmd, | ||
3160 | }, | ||
3161 | }; | ||
3162 | |||
3163 | static int __init ip_vs_genl_register(void) | ||
3164 | { | ||
3165 | int ret, i; | ||
3166 | |||
3167 | ret = genl_register_family(&ip_vs_genl_family); | ||
3168 | if (ret) | ||
3169 | return ret; | ||
3170 | |||
3171 | for (i = 0; i < ARRAY_SIZE(ip_vs_genl_ops); i++) { | ||
3172 | ret = genl_register_ops(&ip_vs_genl_family, &ip_vs_genl_ops[i]); | ||
3173 | if (ret) | ||
3174 | goto err_out; | ||
3175 | } | ||
3176 | return 0; | ||
3177 | |||
3178 | err_out: | ||
3179 | genl_unregister_family(&ip_vs_genl_family); | ||
3180 | return ret; | ||
3181 | } | ||
3182 | |||
3183 | static void ip_vs_genl_unregister(void) | ||
3184 | { | ||
3185 | genl_unregister_family(&ip_vs_genl_family); | ||
3186 | } | ||
3187 | |||
3188 | /* End of Generic Netlink interface definitions */ | ||
3189 | |||
2323 | 3190 | ||
2324 | int __init ip_vs_control_init(void) | 3191 | int __init ip_vs_control_init(void) |
2325 | { | 3192 | { |
@@ -2334,6 +3201,13 @@ int __init ip_vs_control_init(void) | |||
2334 | return ret; | 3201 | return ret; |
2335 | } | 3202 | } |
2336 | 3203 | ||
3204 | ret = ip_vs_genl_register(); | ||
3205 | if (ret) { | ||
3206 | IP_VS_ERR("cannot register Generic Netlink interface.\n"); | ||
3207 | nf_unregister_sockopt(&ip_vs_sockopts); | ||
3208 | return ret; | ||
3209 | } | ||
3210 | |||
2337 | proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops); | 3211 | proc_net_fops_create(&init_net, "ip_vs", 0, &ip_vs_info_fops); |
2338 | proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops); | 3212 | proc_net_fops_create(&init_net, "ip_vs_stats",0, &ip_vs_stats_fops); |
2339 | 3213 | ||
@@ -2368,6 +3242,7 @@ void ip_vs_control_cleanup(void) | |||
2368 | unregister_sysctl_table(sysctl_header); | 3242 | unregister_sysctl_table(sysctl_header); |
2369 | proc_net_remove(&init_net, "ip_vs_stats"); | 3243 | proc_net_remove(&init_net, "ip_vs_stats"); |
2370 | proc_net_remove(&init_net, "ip_vs"); | 3244 | proc_net_remove(&init_net, "ip_vs"); |
3245 | ip_vs_genl_unregister(); | ||
2371 | nf_unregister_sockopt(&ip_vs_sockopts); | 3246 | nf_unregister_sockopt(&ip_vs_sockopts); |
2372 | LeaveFunction(2); | 3247 | LeaveFunction(2); |
2373 | } | 3248 | } |