diff options
| author | David S. Miller <davem@davemloft.net> | 2015-04-12 21:25:14 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2015-04-12 21:25:14 -0400 |
| commit | 1ceb0b8ca5be40d32b0fcf57b117cfb595c9bbfa (patch) | |
| tree | 3593ce71a5a16427c35823a073072292a0d2a704 | |
| parent | d72da6aa00497ed86836a1d41fc55e2809a8ca18 (diff) | |
| parent | 7a6c8c34e5b71ac50e39588e20b39494a9e1d8e5 (diff) | |
Merge branch 'fou-next'
Cong Wang says:
====================
fou: some fixes and updates
Patch 1~3 fix some minor bugs in net/ipv4/fou.c, the only
thing I am not sure is if it's too late to change the
byte order of FOU_ATTR_PORT, if so we have to fix iproute2
instead of kernel.
Patch 4~5 add some new features to make it complete.
v2: make fou->port be16 too
====================
Acked-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
| -rw-r--r-- | include/uapi/linux/fou.h | 1 | ||||
| -rw-r--r-- | net/ipv4/fou.c | 230 |
2 files changed, 186 insertions, 45 deletions
diff --git a/include/uapi/linux/fou.h b/include/uapi/linux/fou.h index c303588bb767..d2947c52dc67 100644 --- a/include/uapi/linux/fou.h +++ b/include/uapi/linux/fou.h | |||
| @@ -25,6 +25,7 @@ enum { | |||
| 25 | FOU_CMD_UNSPEC, | 25 | FOU_CMD_UNSPEC, |
| 26 | FOU_CMD_ADD, | 26 | FOU_CMD_ADD, |
| 27 | FOU_CMD_DEL, | 27 | FOU_CMD_DEL, |
| 28 | FOU_CMD_GET, | ||
| 28 | 29 | ||
| 29 | __FOU_CMD_MAX, | 30 | __FOU_CMD_MAX, |
| 30 | }; | 31 | }; |
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c index ff069f6597ac..263710259774 100644 --- a/net/ipv4/fou.c +++ b/net/ipv4/fou.c | |||
| @@ -16,14 +16,12 @@ | |||
| 16 | #include <uapi/linux/fou.h> | 16 | #include <uapi/linux/fou.h> |
| 17 | #include <uapi/linux/genetlink.h> | 17 | #include <uapi/linux/genetlink.h> |
| 18 | 18 | ||
| 19 | static DEFINE_SPINLOCK(fou_lock); | ||
| 20 | static LIST_HEAD(fou_list); | ||
| 21 | |||
| 22 | struct fou { | 19 | struct fou { |
| 23 | struct socket *sock; | 20 | struct socket *sock; |
| 24 | u8 protocol; | 21 | u8 protocol; |
| 25 | u8 flags; | 22 | u8 flags; |
| 26 | u16 port; | 23 | __be16 port; |
| 24 | u16 type; | ||
| 27 | struct udp_offload udp_offloads; | 25 | struct udp_offload udp_offloads; |
| 28 | struct list_head list; | 26 | struct list_head list; |
| 29 | }; | 27 | }; |
| @@ -37,6 +35,13 @@ struct fou_cfg { | |||
| 37 | struct udp_port_cfg udp_config; | 35 | struct udp_port_cfg udp_config; |
| 38 | }; | 36 | }; |
| 39 | 37 | ||
| 38 | static unsigned int fou_net_id; | ||
| 39 | |||
| 40 | struct fou_net { | ||
| 41 | struct list_head fou_list; | ||
| 42 | struct mutex fou_lock; | ||
| 43 | }; | ||
| 44 | |||
| 40 | static inline struct fou *fou_from_sock(struct sock *sk) | 45 | static inline struct fou *fou_from_sock(struct sock *sk) |
| 41 | { | 46 | { |
| 42 | return sk->sk_user_data; | 47 | return sk->sk_user_data; |
| @@ -387,20 +392,21 @@ out_unlock: | |||
| 387 | return err; | 392 | return err; |
| 388 | } | 393 | } |
| 389 | 394 | ||
| 390 | static int fou_add_to_port_list(struct fou *fou) | 395 | static int fou_add_to_port_list(struct net *net, struct fou *fou) |
| 391 | { | 396 | { |
| 397 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 392 | struct fou *fout; | 398 | struct fou *fout; |
| 393 | 399 | ||
| 394 | spin_lock(&fou_lock); | 400 | mutex_lock(&fn->fou_lock); |
| 395 | list_for_each_entry(fout, &fou_list, list) { | 401 | list_for_each_entry(fout, &fn->fou_list, list) { |
| 396 | if (fou->port == fout->port) { | 402 | if (fou->port == fout->port) { |
| 397 | spin_unlock(&fou_lock); | 403 | mutex_unlock(&fn->fou_lock); |
| 398 | return -EALREADY; | 404 | return -EALREADY; |
| 399 | } | 405 | } |
| 400 | } | 406 | } |
| 401 | 407 | ||
| 402 | list_add(&fou->list, &fou_list); | 408 | list_add(&fou->list, &fn->fou_list); |
| 403 | spin_unlock(&fou_lock); | 409 | mutex_unlock(&fn->fou_lock); |
| 404 | 410 | ||
| 405 | return 0; | 411 | return 0; |
| 406 | } | 412 | } |
| @@ -410,14 +416,10 @@ static void fou_release(struct fou *fou) | |||
| 410 | struct socket *sock = fou->sock; | 416 | struct socket *sock = fou->sock; |
| 411 | struct sock *sk = sock->sk; | 417 | struct sock *sk = sock->sk; |
| 412 | 418 | ||
| 413 | udp_del_offload(&fou->udp_offloads); | 419 | if (sk->sk_family == AF_INET) |
| 414 | 420 | udp_del_offload(&fou->udp_offloads); | |
| 415 | list_del(&fou->list); | 421 | list_del(&fou->list); |
| 416 | 422 | udp_tunnel_sock_release(sock); | |
| 417 | /* Remove hooks into tunnel socket */ | ||
| 418 | sk->sk_user_data = NULL; | ||
| 419 | |||
| 420 | sock_release(sock); | ||
| 421 | 423 | ||
| 422 | kfree(fou); | 424 | kfree(fou); |
| 423 | } | 425 | } |
| @@ -447,10 +449,10 @@ static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) | |||
| 447 | static int fou_create(struct net *net, struct fou_cfg *cfg, | 449 | static int fou_create(struct net *net, struct fou_cfg *cfg, |
| 448 | struct socket **sockp) | 450 | struct socket **sockp) |
| 449 | { | 451 | { |
| 450 | struct fou *fou = NULL; | ||
| 451 | int err; | ||
| 452 | struct socket *sock = NULL; | 452 | struct socket *sock = NULL; |
| 453 | struct fou *fou = NULL; | ||
| 453 | struct sock *sk; | 454 | struct sock *sk; |
| 455 | int err; | ||
| 454 | 456 | ||
| 455 | /* Open UDP socket */ | 457 | /* Open UDP socket */ |
| 456 | err = udp_sock_create(net, &cfg->udp_config, &sock); | 458 | err = udp_sock_create(net, &cfg->udp_config, &sock); |
| @@ -486,6 +488,8 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 486 | goto error; | 488 | goto error; |
| 487 | } | 489 | } |
| 488 | 490 | ||
| 491 | fou->type = cfg->type; | ||
| 492 | |||
| 489 | udp_sk(sk)->encap_type = 1; | 493 | udp_sk(sk)->encap_type = 1; |
| 490 | udp_encap_enable(); | 494 | udp_encap_enable(); |
| 491 | 495 | ||
| @@ -502,7 +506,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 502 | goto error; | 506 | goto error; |
| 503 | } | 507 | } |
| 504 | 508 | ||
| 505 | err = fou_add_to_port_list(fou); | 509 | err = fou_add_to_port_list(net, fou); |
| 506 | if (err) | 510 | if (err) |
| 507 | goto error; | 511 | goto error; |
| 508 | 512 | ||
| @@ -514,27 +518,27 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, | |||
| 514 | error: | 518 | error: |
| 515 | kfree(fou); | 519 | kfree(fou); |
| 516 | if (sock) | 520 | if (sock) |
| 517 | sock_release(sock); | 521 | udp_tunnel_sock_release(sock); |
| 518 | 522 | ||
| 519 | return err; | 523 | return err; |
| 520 | } | 524 | } |
| 521 | 525 | ||
| 522 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) | 526 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) |
| 523 | { | 527 | { |
| 524 | struct fou *fou; | 528 | struct fou_net *fn = net_generic(net, fou_net_id); |
| 525 | u16 port = cfg->udp_config.local_udp_port; | 529 | __be16 port = cfg->udp_config.local_udp_port; |
| 526 | int err = -EINVAL; | 530 | int err = -EINVAL; |
| 531 | struct fou *fou; | ||
| 527 | 532 | ||
| 528 | spin_lock(&fou_lock); | 533 | mutex_lock(&fn->fou_lock); |
| 529 | list_for_each_entry(fou, &fou_list, list) { | 534 | list_for_each_entry(fou, &fn->fou_list, list) { |
| 530 | if (fou->port == port) { | 535 | if (fou->port == port) { |
| 531 | udp_del_offload(&fou->udp_offloads); | ||
| 532 | fou_release(fou); | 536 | fou_release(fou); |
| 533 | err = 0; | 537 | err = 0; |
| 534 | break; | 538 | break; |
| 535 | } | 539 | } |
| 536 | } | 540 | } |
| 537 | spin_unlock(&fou_lock); | 541 | mutex_unlock(&fn->fou_lock); |
| 538 | 542 | ||
| 539 | return err; | 543 | return err; |
| 540 | } | 544 | } |
| @@ -573,7 +577,7 @@ static int parse_nl_config(struct genl_info *info, | |||
| 573 | } | 577 | } |
| 574 | 578 | ||
| 575 | if (info->attrs[FOU_ATTR_PORT]) { | 579 | if (info->attrs[FOU_ATTR_PORT]) { |
| 576 | u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]); | 580 | __be16 port = nla_get_be16(info->attrs[FOU_ATTR_PORT]); |
| 577 | 581 | ||
| 578 | cfg->udp_config.local_udp_port = port; | 582 | cfg->udp_config.local_udp_port = port; |
| 579 | } | 583 | } |
| @@ -592,6 +596,7 @@ static int parse_nl_config(struct genl_info *info, | |||
| 592 | 596 | ||
| 593 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | 597 | static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) |
| 594 | { | 598 | { |
| 599 | struct net *net = genl_info_net(info); | ||
| 595 | struct fou_cfg cfg; | 600 | struct fou_cfg cfg; |
| 596 | int err; | 601 | int err; |
| 597 | 602 | ||
| @@ -599,16 +604,120 @@ static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) | |||
| 599 | if (err) | 604 | if (err) |
| 600 | return err; | 605 | return err; |
| 601 | 606 | ||
| 602 | return fou_create(&init_net, &cfg, NULL); | 607 | return fou_create(net, &cfg, NULL); |
| 603 | } | 608 | } |
| 604 | 609 | ||
| 605 | static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) | 610 | static int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info) |
| 606 | { | 611 | { |
| 612 | struct net *net = genl_info_net(info); | ||
| 613 | struct fou_cfg cfg; | ||
| 614 | int err; | ||
| 615 | |||
| 616 | err = parse_nl_config(info, &cfg); | ||
| 617 | if (err) | ||
| 618 | return err; | ||
| 619 | |||
| 620 | return fou_destroy(net, &cfg); | ||
| 621 | } | ||
| 622 | |||
| 623 | static int fou_fill_info(struct fou *fou, struct sk_buff *msg) | ||
| 624 | { | ||
| 625 | if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) || | ||
| 626 | nla_put_be16(msg, FOU_ATTR_PORT, fou->port) || | ||
| 627 | nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) || | ||
| 628 | nla_put_u8(msg, FOU_ATTR_TYPE, fou->type)) | ||
| 629 | return -1; | ||
| 630 | |||
| 631 | if (fou->flags & FOU_F_REMCSUM_NOPARTIAL) | ||
| 632 | if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL)) | ||
| 633 | return -1; | ||
| 634 | return 0; | ||
| 635 | } | ||
| 636 | |||
| 637 | static int fou_dump_info(struct fou *fou, u32 portid, u32 seq, | ||
| 638 | u32 flags, struct sk_buff *skb, u8 cmd) | ||
| 639 | { | ||
| 640 | void *hdr; | ||
| 641 | |||
| 642 | hdr = genlmsg_put(skb, portid, seq, &fou_nl_family, flags, cmd); | ||
| 643 | if (!hdr) | ||
| 644 | return -ENOMEM; | ||
| 645 | |||
| 646 | if (fou_fill_info(fou, skb) < 0) | ||
| 647 | goto nla_put_failure; | ||
| 648 | |||
| 649 | genlmsg_end(skb, hdr); | ||
| 650 | return 0; | ||
| 651 | |||
| 652 | nla_put_failure: | ||
| 653 | genlmsg_cancel(skb, hdr); | ||
| 654 | return -EMSGSIZE; | ||
| 655 | } | ||
| 656 | |||
| 657 | static int fou_nl_cmd_get_port(struct sk_buff *skb, struct genl_info *info) | ||
| 658 | { | ||
| 659 | struct net *net = genl_info_net(info); | ||
| 660 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 661 | struct sk_buff *msg; | ||
| 607 | struct fou_cfg cfg; | 662 | struct fou_cfg cfg; |
| 663 | struct fou *fout; | ||
| 664 | __be16 port; | ||
| 665 | int ret; | ||
| 666 | |||
| 667 | ret = parse_nl_config(info, &cfg); | ||
| 668 | if (ret) | ||
| 669 | return ret; | ||
| 670 | port = cfg.udp_config.local_udp_port; | ||
| 671 | if (port == 0) | ||
| 672 | return -EINVAL; | ||
| 673 | |||
| 674 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 675 | if (!msg) | ||
| 676 | return -ENOMEM; | ||
| 677 | |||
| 678 | ret = -ESRCH; | ||
| 679 | mutex_lock(&fn->fou_lock); | ||
| 680 | list_for_each_entry(fout, &fn->fou_list, list) { | ||
| 681 | if (port == fout->port) { | ||
| 682 | ret = fou_dump_info(fout, info->snd_portid, | ||
| 683 | info->snd_seq, 0, msg, | ||
| 684 | info->genlhdr->cmd); | ||
| 685 | break; | ||
| 686 | } | ||
| 687 | } | ||
| 688 | mutex_unlock(&fn->fou_lock); | ||
| 689 | if (ret < 0) | ||
| 690 | goto out_free; | ||
| 608 | 691 | ||
| 609 | parse_nl_config(info, &cfg); | 692 | return genlmsg_reply(msg, info); |
| 610 | 693 | ||
| 611 | return fou_destroy(&init_net, &cfg); | 694 | out_free: |
| 695 | nlmsg_free(msg); | ||
| 696 | return ret; | ||
| 697 | } | ||
| 698 | |||
| 699 | static int fou_nl_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||
| 700 | { | ||
| 701 | struct net *net = sock_net(skb->sk); | ||
| 702 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 703 | struct fou *fout; | ||
| 704 | int idx = 0, ret; | ||
| 705 | |||
| 706 | mutex_lock(&fn->fou_lock); | ||
| 707 | list_for_each_entry(fout, &fn->fou_list, list) { | ||
| 708 | if (idx++ < cb->args[0]) | ||
| 709 | continue; | ||
| 710 | ret = fou_dump_info(fout, NETLINK_CB(cb->skb).portid, | ||
| 711 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
| 712 | skb, FOU_CMD_GET); | ||
| 713 | if (ret) | ||
| 714 | goto done; | ||
| 715 | } | ||
| 716 | mutex_unlock(&fn->fou_lock); | ||
| 717 | |||
| 718 | done: | ||
| 719 | cb->args[0] = idx; | ||
| 720 | return skb->len; | ||
| 612 | } | 721 | } |
| 613 | 722 | ||
| 614 | static const struct genl_ops fou_nl_ops[] = { | 723 | static const struct genl_ops fou_nl_ops[] = { |
| @@ -624,6 +733,12 @@ static const struct genl_ops fou_nl_ops[] = { | |||
| 624 | .policy = fou_nl_policy, | 733 | .policy = fou_nl_policy, |
| 625 | .flags = GENL_ADMIN_PERM, | 734 | .flags = GENL_ADMIN_PERM, |
| 626 | }, | 735 | }, |
| 736 | { | ||
| 737 | .cmd = FOU_CMD_GET, | ||
| 738 | .doit = fou_nl_cmd_get_port, | ||
| 739 | .dumpit = fou_nl_dump, | ||
| 740 | .policy = fou_nl_policy, | ||
| 741 | }, | ||
| 627 | }; | 742 | }; |
| 628 | 743 | ||
| 629 | size_t fou_encap_hlen(struct ip_tunnel_encap *e) | 744 | size_t fou_encap_hlen(struct ip_tunnel_encap *e) |
| @@ -820,38 +935,63 @@ static void ip_tunnel_encap_del_fou_ops(void) | |||
| 820 | 935 | ||
| 821 | #endif | 936 | #endif |
| 822 | 937 | ||
| 938 | static __net_init int fou_init_net(struct net *net) | ||
| 939 | { | ||
| 940 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 941 | |||
| 942 | INIT_LIST_HEAD(&fn->fou_list); | ||
| 943 | mutex_init(&fn->fou_lock); | ||
| 944 | return 0; | ||
| 945 | } | ||
| 946 | |||
| 947 | static __net_exit void fou_exit_net(struct net *net) | ||
| 948 | { | ||
| 949 | struct fou_net *fn = net_generic(net, fou_net_id); | ||
| 950 | struct fou *fou, *next; | ||
| 951 | |||
| 952 | /* Close all the FOU sockets */ | ||
| 953 | mutex_lock(&fn->fou_lock); | ||
| 954 | list_for_each_entry_safe(fou, next, &fn->fou_list, list) | ||
| 955 | fou_release(fou); | ||
| 956 | mutex_unlock(&fn->fou_lock); | ||
| 957 | } | ||
| 958 | |||
| 959 | static struct pernet_operations fou_net_ops = { | ||
| 960 | .init = fou_init_net, | ||
| 961 | .exit = fou_exit_net, | ||
| 962 | .id = &fou_net_id, | ||
| 963 | .size = sizeof(struct fou_net), | ||
| 964 | }; | ||
| 965 | |||
| 823 | static int __init fou_init(void) | 966 | static int __init fou_init(void) |
| 824 | { | 967 | { |
| 825 | int ret; | 968 | int ret; |
| 826 | 969 | ||
| 970 | ret = register_pernet_device(&fou_net_ops); | ||
| 971 | if (ret) | ||
| 972 | goto exit; | ||
| 973 | |||
| 827 | ret = genl_register_family_with_ops(&fou_nl_family, | 974 | ret = genl_register_family_with_ops(&fou_nl_family, |
| 828 | fou_nl_ops); | 975 | fou_nl_ops); |
| 829 | |||
| 830 | if (ret < 0) | 976 | if (ret < 0) |
| 831 | goto exit; | 977 | goto unregister; |
| 832 | 978 | ||
| 833 | ret = ip_tunnel_encap_add_fou_ops(); | 979 | ret = ip_tunnel_encap_add_fou_ops(); |
| 834 | if (ret < 0) | 980 | if (ret == 0) |
| 835 | genl_unregister_family(&fou_nl_family); | 981 | return 0; |
| 836 | 982 | ||
| 983 | genl_unregister_family(&fou_nl_family); | ||
| 984 | unregister: | ||
| 985 | unregister_pernet_device(&fou_net_ops); | ||
| 837 | exit: | 986 | exit: |
| 838 | return ret; | 987 | return ret; |
| 839 | } | 988 | } |
| 840 | 989 | ||
| 841 | static void __exit fou_fini(void) | 990 | static void __exit fou_fini(void) |
| 842 | { | 991 | { |
| 843 | struct fou *fou, *next; | ||
| 844 | |||
| 845 | ip_tunnel_encap_del_fou_ops(); | 992 | ip_tunnel_encap_del_fou_ops(); |
| 846 | |||
| 847 | genl_unregister_family(&fou_nl_family); | 993 | genl_unregister_family(&fou_nl_family); |
| 848 | 994 | unregister_pernet_device(&fou_net_ops); | |
| 849 | /* Close all the FOU sockets */ | ||
| 850 | |||
| 851 | spin_lock(&fou_lock); | ||
| 852 | list_for_each_entry_safe(fou, next, &fou_list, list) | ||
| 853 | fou_release(fou); | ||
| 854 | spin_unlock(&fou_lock); | ||
| 855 | } | 995 | } |
| 856 | 996 | ||
| 857 | module_init(fou_init); | 997 | module_init(fou_init); |
