diff options
author | Nicolas Dichtel <nicolas.dichtel@6wind.com> | 2018-11-26 09:42:04 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-11-27 19:20:20 -0500 |
commit | cff478b9d9ccaee0de0e02700c63addf007b5d3c (patch) | |
tree | 46a092a1086d179fa5ef72cbc56901ee68419567 /net/core/net_namespace.c | |
parent | a0732ad14d40ee7562c8c6e04b01af6134c82831 (diff) |
netns: add support of NETNSA_TARGET_NSID
Like it was done for link and address, add the ability to perform get/dump
in another netns by specifying a target nsid attribute.
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/net_namespace.c')
-rw-r--r-- | net/core/net_namespace.c | 86 |
1 files changed, 75 insertions, 11 deletions
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index f8a5966b086c..885c54197e31 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c | |||
@@ -669,6 +669,7 @@ static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { | |||
669 | [NETNSA_NSID] = { .type = NLA_S32 }, | 669 | [NETNSA_NSID] = { .type = NLA_S32 }, |
670 | [NETNSA_PID] = { .type = NLA_U32 }, | 670 | [NETNSA_PID] = { .type = NLA_U32 }, |
671 | [NETNSA_FD] = { .type = NLA_U32 }, | 671 | [NETNSA_FD] = { .type = NLA_U32 }, |
672 | [NETNSA_TARGET_NSID] = { .type = NLA_S32 }, | ||
672 | }; | 673 | }; |
673 | 674 | ||
674 | static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh, | 675 | static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh, |
@@ -780,9 +781,10 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
780 | .seq = nlh->nlmsg_seq, | 781 | .seq = nlh->nlmsg_seq, |
781 | .cmd = RTM_NEWNSID, | 782 | .cmd = RTM_NEWNSID, |
782 | }; | 783 | }; |
784 | struct net *peer, *target = net; | ||
785 | bool put_target = false; | ||
783 | struct nlattr *nla; | 786 | struct nlattr *nla; |
784 | struct sk_buff *msg; | 787 | struct sk_buff *msg; |
785 | struct net *peer; | ||
786 | int err; | 788 | int err; |
787 | 789 | ||
788 | err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, | 790 | err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, |
@@ -806,13 +808,27 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
806 | return PTR_ERR(peer); | 808 | return PTR_ERR(peer); |
807 | } | 809 | } |
808 | 810 | ||
811 | if (tb[NETNSA_TARGET_NSID]) { | ||
812 | int id = nla_get_s32(tb[NETNSA_TARGET_NSID]); | ||
813 | |||
814 | target = rtnl_get_net_ns_capable(NETLINK_CB(skb).sk, id); | ||
815 | if (IS_ERR(target)) { | ||
816 | NL_SET_BAD_ATTR(extack, tb[NETNSA_TARGET_NSID]); | ||
817 | NL_SET_ERR_MSG(extack, | ||
818 | "Target netns reference is invalid"); | ||
819 | err = PTR_ERR(target); | ||
820 | goto out; | ||
821 | } | ||
822 | put_target = true; | ||
823 | } | ||
824 | |||
809 | msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); | 825 | msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); |
810 | if (!msg) { | 826 | if (!msg) { |
811 | err = -ENOMEM; | 827 | err = -ENOMEM; |
812 | goto out; | 828 | goto out; |
813 | } | 829 | } |
814 | 830 | ||
815 | fillargs.nsid = peernet2id(net, peer); | 831 | fillargs.nsid = peernet2id(target, peer); |
816 | err = rtnl_net_fill(msg, &fillargs); | 832 | err = rtnl_net_fill(msg, &fillargs); |
817 | if (err < 0) | 833 | if (err < 0) |
818 | goto err_out; | 834 | goto err_out; |
@@ -823,15 +839,19 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
823 | err_out: | 839 | err_out: |
824 | nlmsg_free(msg); | 840 | nlmsg_free(msg); |
825 | out: | 841 | out: |
842 | if (put_target) | ||
843 | put_net(target); | ||
826 | put_net(peer); | 844 | put_net(peer); |
827 | return err; | 845 | return err; |
828 | } | 846 | } |
829 | 847 | ||
830 | struct rtnl_net_dump_cb { | 848 | struct rtnl_net_dump_cb { |
849 | struct net *tgt_net; | ||
831 | struct sk_buff *skb; | 850 | struct sk_buff *skb; |
832 | struct net_fill_args fillargs; | 851 | struct net_fill_args fillargs; |
833 | int idx; | 852 | int idx; |
834 | int s_idx; | 853 | int s_idx; |
854 | bool put_tgt_net; | ||
835 | }; | 855 | }; |
836 | 856 | ||
837 | static int rtnl_net_dumpid_one(int id, void *peer, void *data) | 857 | static int rtnl_net_dumpid_one(int id, void *peer, void *data) |
@@ -852,10 +872,50 @@ cont: | |||
852 | return 0; | 872 | return 0; |
853 | } | 873 | } |
854 | 874 | ||
875 | static int rtnl_valid_dump_net_req(const struct nlmsghdr *nlh, struct sock *sk, | ||
876 | struct rtnl_net_dump_cb *net_cb, | ||
877 | struct netlink_callback *cb) | ||
878 | { | ||
879 | struct netlink_ext_ack *extack = cb->extack; | ||
880 | struct nlattr *tb[NETNSA_MAX + 1]; | ||
881 | int err, i; | ||
882 | |||
883 | err = nlmsg_parse_strict(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, | ||
884 | rtnl_net_policy, extack); | ||
885 | if (err < 0) | ||
886 | return err; | ||
887 | |||
888 | for (i = 0; i <= NETNSA_MAX; i++) { | ||
889 | if (!tb[i]) | ||
890 | continue; | ||
891 | |||
892 | if (i == NETNSA_TARGET_NSID) { | ||
893 | struct net *net; | ||
894 | |||
895 | net = rtnl_get_net_ns_capable(sk, nla_get_s32(tb[i])); | ||
896 | if (IS_ERR(net)) { | ||
897 | NL_SET_BAD_ATTR(extack, tb[i]); | ||
898 | NL_SET_ERR_MSG(extack, | ||
899 | "Invalid target network namespace id"); | ||
900 | return PTR_ERR(net); | ||
901 | } | ||
902 | net_cb->tgt_net = net; | ||
903 | net_cb->put_tgt_net = true; | ||
904 | } else { | ||
905 | NL_SET_BAD_ATTR(extack, tb[i]); | ||
906 | NL_SET_ERR_MSG(extack, | ||
907 | "Unsupported attribute in dump request"); | ||
908 | return -EINVAL; | ||
909 | } | ||
910 | } | ||
911 | |||
912 | return 0; | ||
913 | } | ||
914 | |||
855 | static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) | 915 | static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) |
856 | { | 916 | { |
857 | struct net *net = sock_net(skb->sk); | ||
858 | struct rtnl_net_dump_cb net_cb = { | 917 | struct rtnl_net_dump_cb net_cb = { |
918 | .tgt_net = sock_net(skb->sk), | ||
859 | .skb = skb, | 919 | .skb = skb, |
860 | .fillargs = { | 920 | .fillargs = { |
861 | .portid = NETLINK_CB(cb->skb).portid, | 921 | .portid = NETLINK_CB(cb->skb).portid, |
@@ -866,19 +926,23 @@ static int rtnl_net_dumpid(struct sk_buff *skb, struct netlink_callback *cb) | |||
866 | .idx = 0, | 926 | .idx = 0, |
867 | .s_idx = cb->args[0], | 927 | .s_idx = cb->args[0], |
868 | }; | 928 | }; |
929 | int err = 0; | ||
869 | 930 | ||
870 | if (cb->strict_check && | 931 | if (cb->strict_check) { |
871 | nlmsg_attrlen(cb->nlh, sizeof(struct rtgenmsg))) { | 932 | err = rtnl_valid_dump_net_req(cb->nlh, skb->sk, &net_cb, cb); |
872 | NL_SET_ERR_MSG(cb->extack, "Unknown data in network namespace id dump request"); | 933 | if (err < 0) |
873 | return -EINVAL; | 934 | goto end; |
874 | } | 935 | } |
875 | 936 | ||
876 | spin_lock_bh(&net->nsid_lock); | 937 | spin_lock_bh(&net_cb.tgt_net->nsid_lock); |
877 | idr_for_each(&net->netns_ids, rtnl_net_dumpid_one, &net_cb); | 938 | idr_for_each(&net_cb.tgt_net->netns_ids, rtnl_net_dumpid_one, &net_cb); |
878 | spin_unlock_bh(&net->nsid_lock); | 939 | spin_unlock_bh(&net_cb.tgt_net->nsid_lock); |
879 | 940 | ||
880 | cb->args[0] = net_cb.idx; | 941 | cb->args[0] = net_cb.idx; |
881 | return skb->len; | 942 | end: |
943 | if (net_cb.put_tgt_net) | ||
944 | put_net(net_cb.tgt_net); | ||
945 | return err < 0 ? err : skb->len; | ||
882 | } | 946 | } |
883 | 947 | ||
884 | static void rtnl_net_notifyid(struct net *net, int cmd, int id) | 948 | static void rtnl_net_notifyid(struct net *net, int cmd, int id) |