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 ====================
Unified · Split
| @@ -25,6 +25,7 @@ enum { · 24 unmodified lines | ||
| 25 | 25 | FOU_CMD_UNSPEC, |
| 26 | 26 | FOU_CMD_ADD, |
| 27 | 27 | FOU_CMD_DEL, |
| 28 | + FOU_CMD_GET, | |
| 28 | 29 | |
| 29 | 30 | __FOU_CMD_MAX, |
| 30 | 31 | }; |
| @@ -16,14 +16,12 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <uapi/linux/fou.h> |
| 17 | 17 | #include <uapi/linux/genetlink.h> |
| 18 | 18 | |
| 19 | −static DEFINE_SPINLOCK(fou_lock); | |
| 20 | −static LIST_HEAD(fou_list); | |
| 21 | − | |
| 22 | 19 | struct fou { |
| 23 | 20 | struct socket *sock; |
| 24 | 21 | u8 protocol; |
| 25 | 22 | u8 flags; |
| 26 | − u16 port; | |
| 23 | + __be16 port; | |
| 24 | + u16 type; | |
| 27 | 25 | struct udp_offload udp_offloads; |
| 28 | 26 | struct list_head list; |
| 29 | 27 | }; |
| @@ -37,6 +35,13 @@ struct fou_cfg { · 7 unmodified lines | ||
| 37 | 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 | 45 | static inline struct fou *fou_from_sock(struct sock *sk) |
| 41 | 46 | { |
| 42 | 47 | return sk->sk_user_data; |
| @@ -387,20 +392,21 @@ out_unlock: · 344 unmodified lines | ||
| 387 | 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 | 398 | struct fou *fout; |
| 393 | 399 | |
| 394 | − spin_lock(&fou_lock); | |
| 395 | − list_for_each_entry(fout, &fou_list, list) { | |
| 400 | + mutex_lock(&fn->fou_lock); | |
| 401 | + list_for_each_entry(fout, &fn->fou_list, list) { | |
| 396 | 402 | if (fou->port == fout->port) { |
| 397 | − spin_unlock(&fou_lock); | |
| 403 | + mutex_unlock(&fn->fou_lock); | |
| 398 | 404 | return -EALREADY; |
| 399 | 405 | } |
| 400 | 406 | } |
| 401 | 407 | |
| 402 | − list_add(&fou->list, &fou_list); | |
| 403 | − spin_unlock(&fou_lock); | |
| 408 | + list_add(&fou->list, &fn->fou_list); | |
| 409 | + mutex_unlock(&fn->fou_lock); | |
| 404 | 410 | |
| 405 | 411 | return 0; |
| 406 | 412 | } |
| @@ -410,14 +416,10 @@ static void fou_release(struct fou *fou) · 3 unmodified lines | ||
| 410 | 416 | struct socket *sock = fou->sock; |
| 411 | 417 | struct sock *sk = sock->sk; |
| 412 | 418 | |
| 413 | − udp_del_offload(&fou->udp_offloads); | |
| 414 | − | |
| 419 | + if (sk->sk_family == AF_INET) | |
| 420 | + udp_del_offload(&fou->udp_offloads); | |
| 415 | 421 | list_del(&fou->list); |
| 416 | − | |
| 417 | − /* Remove hooks into tunnel socket */ | |
| 418 | − sk->sk_user_data = NULL; | |
| 419 | − | |
| 420 | − sock_release(sock); | |
| 422 | + udp_tunnel_sock_release(sock); | |
| 421 | 423 | |
| 422 | 424 | kfree(fou); |
| 423 | 425 | } |
| @@ -447,10 +449,10 @@ static int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg) · 23 unmodified lines | ||
| 447 | 449 | static int fou_create(struct net *net, struct fou_cfg *cfg, |
| 448 | 450 | struct socket **sockp) |
| 449 | 451 | { |
| 450 | − struct fou *fou = NULL; | |
| 451 | − int err; | |
| 452 | 452 | struct socket *sock = NULL; |
| 453 | + struct fou *fou = NULL; | |
| 453 | 454 | struct sock *sk; |
| 455 | + int err; | |
| 454 | 456 | |
| 455 | 457 | /* Open UDP socket */ |
| 456 | 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, · 29 unmodified lines | ||
| 486 | 488 | goto error; |
| 487 | 489 | } |
| 488 | 490 | |
| 491 | + fou->type = cfg->type; | |
| 492 | + | |
| 489 | 493 | udp_sk(sk)->encap_type = 1; |
| 490 | 494 | udp_encap_enable(); |
| 491 | 495 | |
| @@ -502,7 +506,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, · 10 unmodified lines | ||
| 502 | 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 | 510 | if (err) |
| 507 | 511 | goto error; |
| 508 | 512 | |
| @@ -514,27 +518,27 @@ static int fou_create(struct net *net, struct fou_cfg *cfg, · 5 unmodified lines | ||
| 514 | 518 | error: |
| 515 | 519 | kfree(fou); |
| 516 | 520 | if (sock) |
| 517 | − sock_release(sock); | |
| 521 | + udp_tunnel_sock_release(sock); | |
| 518 | 522 | |
| 519 | 523 | return err; |
| 520 | 524 | } |
| 521 | 525 | |
| 522 | 526 | static int fou_destroy(struct net *net, struct fou_cfg *cfg) |
| 523 | 527 | { |
| 524 | − struct fou *fou; | |
| 525 | − u16 port = cfg->udp_config.local_udp_port; | |
| 528 | + struct fou_net *fn = net_generic(net, fou_net_id); | |
| 529 | + __be16 port = cfg->udp_config.local_udp_port; | |
| 526 | 530 | int err = -EINVAL; |
| 531 | + struct fou *fou; | |
| 527 | 532 | |
| 528 | − spin_lock(&fou_lock); | |
| 529 | − list_for_each_entry(fou, &fou_list, list) { | |
| 533 | + mutex_lock(&fn->fou_lock); | |
| 534 | + list_for_each_entry(fou, &fn->fou_list, list) { | |
| 530 | 535 | if (fou->port == port) { |
| 531 | − udp_del_offload(&fou->udp_offloads); | |
| 532 | 536 | fou_release(fou); |
| 533 | 537 | err = 0; |
| 534 | 538 | break; |
| 535 | 539 | } |
| 536 | 540 | } |
| 537 | − spin_unlock(&fou_lock); | |
| 541 | + mutex_unlock(&fn->fou_lock); | |
| 538 | 542 | |
| 539 | 543 | return err; |
| 540 | 544 | } |
| @@ -573,7 +577,7 @@ static int parse_nl_config(struct genl_info *info, · 32 unmodified lines | ||
| 573 | 577 | } |
| 574 | 578 | |
| 575 | 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 | 582 | cfg->udp_config.local_udp_port = port; |
| 579 | 583 | } |
| @@ -592,6 +596,7 @@ static int parse_nl_config(struct genl_info *info, · 12 unmodified lines | ||
| 592 | 596 | |
| 593 | 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 | 600 | struct fou_cfg cfg; |
| 596 | 601 | int err; |
| 597 | 602 | |
| @@ -599,16 +604,120 @@ static int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info) · 1 unmodified line | ||
| 599 | 604 | if (err) |
| 600 | 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 | 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 | 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 | 723 | static const struct genl_ops fou_nl_ops[] = { |
| @@ -624,6 +733,12 @@ static const struct genl_ops fou_nl_ops[] = { · 9 unmodified lines | ||
| 624 | 733 | .policy = fou_nl_policy, |
| 625 | 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 | 744 | size_t fou_encap_hlen(struct ip_tunnel_encap *e) |
| @@ -820,38 +935,63 @@ static void ip_tunnel_encap_del_fou_ops(void) · 190 unmodified lines | ||
| 820 | 935 | |
| 821 | 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 | 966 | static int __init fou_init(void) |
| 824 | 967 | { |
| 825 | 968 | int ret; |
| 826 | 969 | |
| 970 | + ret = register_pernet_device(&fou_net_ops); | |
| 971 | + if (ret) | |
| 972 | + goto exit; | |
| 973 | + | |
| 827 | 974 | ret = genl_register_family_with_ops(&fou_nl_family, |
| 828 | 975 | fou_nl_ops); |
| 829 | − | |
| 830 | 976 | if (ret < 0) |
| 831 | − goto exit; | |
| 977 | + goto unregister; | |
| 832 | 978 | |
| 833 | 979 | ret = ip_tunnel_encap_add_fou_ops(); |
| 834 | − if (ret < 0) | |
| 835 | − genl_unregister_family(&fou_nl_family); | |
| 980 | + if (ret == 0) | |
| 981 | + return 0; | |
| 836 | 982 | |
| 983 | + genl_unregister_family(&fou_nl_family); | |
| 984 | +unregister: | |
| 985 | + unregister_pernet_device(&fou_net_ops); | |
| 837 | 986 | exit: |
| 838 | 987 | return ret; |
| 839 | 988 | } |
| 840 | 989 | |
| 841 | 990 | static void __exit fou_fini(void) |
| 842 | 991 | { |
| 843 | − struct fou *fou, *next; | |
| 844 | − | |
| 845 | 992 | ip_tunnel_encap_del_fou_ops(); |
| 846 | − | |
| 847 | 993 | genl_unregister_family(&fou_nl_family); |
| 848 | − | |
| 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); | |
| 994 | + unregister_pernet_device(&fou_net_ops); | |
| 855 | 995 | } |
| 856 | 996 | |
| 857 | 997 | module_init(fou_init); |