diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/nf_nat_core.c | 97 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_core.c | 7 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 151 | ||||
-rw-r--r-- | net/netfilter/nfnetlink.c | 12 |
4 files changed, 174 insertions, 93 deletions
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index 2ac9eaf1a8c9..a65cf692359f 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
@@ -584,6 +584,98 @@ static struct nf_ct_ext_type nat_extend __read_mostly = { | |||
584 | .flags = NF_CT_EXT_F_PREALLOC, | 584 | .flags = NF_CT_EXT_F_PREALLOC, |
585 | }; | 585 | }; |
586 | 586 | ||
587 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
588 | |||
589 | #include <linux/netfilter/nfnetlink.h> | ||
590 | #include <linux/netfilter/nfnetlink_conntrack.h> | ||
591 | |||
592 | static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { | ||
593 | [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, | ||
594 | [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, | ||
595 | }; | ||
596 | |||
597 | static int nfnetlink_parse_nat_proto(struct nlattr *attr, | ||
598 | const struct nf_conn *ct, | ||
599 | struct nf_nat_range *range) | ||
600 | { | ||
601 | struct nlattr *tb[CTA_PROTONAT_MAX+1]; | ||
602 | const struct nf_nat_protocol *npt; | ||
603 | int err; | ||
604 | |||
605 | err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); | ||
606 | if (err < 0) | ||
607 | return err; | ||
608 | |||
609 | npt = nf_nat_proto_find_get(nf_ct_protonum(ct)); | ||
610 | if (npt->nlattr_to_range) | ||
611 | err = npt->nlattr_to_range(tb, range); | ||
612 | nf_nat_proto_put(npt); | ||
613 | return err; | ||
614 | } | ||
615 | |||
616 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { | ||
617 | [CTA_NAT_MINIP] = { .type = NLA_U32 }, | ||
618 | [CTA_NAT_MAXIP] = { .type = NLA_U32 }, | ||
619 | }; | ||
620 | |||
621 | static int | ||
622 | nfnetlink_parse_nat(struct nlattr *nat, | ||
623 | const struct nf_conn *ct, struct nf_nat_range *range) | ||
624 | { | ||
625 | struct nlattr *tb[CTA_NAT_MAX+1]; | ||
626 | int err; | ||
627 | |||
628 | memset(range, 0, sizeof(*range)); | ||
629 | |||
630 | err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); | ||
631 | if (err < 0) | ||
632 | return err; | ||
633 | |||
634 | if (tb[CTA_NAT_MINIP]) | ||
635 | range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]); | ||
636 | |||
637 | if (!tb[CTA_NAT_MAXIP]) | ||
638 | range->max_ip = range->min_ip; | ||
639 | else | ||
640 | range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]); | ||
641 | |||
642 | if (range->min_ip) | ||
643 | range->flags |= IP_NAT_RANGE_MAP_IPS; | ||
644 | |||
645 | if (!tb[CTA_NAT_PROTO]) | ||
646 | return 0; | ||
647 | |||
648 | err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); | ||
649 | if (err < 0) | ||
650 | return err; | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int | ||
656 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
657 | enum nf_nat_manip_type manip, | ||
658 | struct nlattr *attr) | ||
659 | { | ||
660 | struct nf_nat_range range; | ||
661 | |||
662 | if (nfnetlink_parse_nat(attr, ct, &range) < 0) | ||
663 | return -EINVAL; | ||
664 | if (nf_nat_initialized(ct, manip)) | ||
665 | return -EEXIST; | ||
666 | |||
667 | return nf_nat_setup_info(ct, &range, manip); | ||
668 | } | ||
669 | #else | ||
670 | static int | ||
671 | nfnetlink_parse_nat_setup(struct nf_conn *ct, | ||
672 | enum nf_nat_manip_type manip, | ||
673 | struct nlattr *attr) | ||
674 | { | ||
675 | return -EOPNOTSUPP; | ||
676 | } | ||
677 | #endif | ||
678 | |||
587 | static int __net_init nf_nat_net_init(struct net *net) | 679 | static int __net_init nf_nat_net_init(struct net *net) |
588 | { | 680 | { |
589 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size, | 681 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size, |
@@ -654,6 +746,9 @@ static int __init nf_nat_init(void) | |||
654 | 746 | ||
655 | BUG_ON(nf_nat_seq_adjust_hook != NULL); | 747 | BUG_ON(nf_nat_seq_adjust_hook != NULL); |
656 | rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); | 748 | rcu_assign_pointer(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); |
749 | BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); | ||
750 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, | ||
751 | nfnetlink_parse_nat_setup); | ||
657 | return 0; | 752 | return 0; |
658 | 753 | ||
659 | cleanup_extend: | 754 | cleanup_extend: |
@@ -667,10 +762,12 @@ static void __exit nf_nat_cleanup(void) | |||
667 | nf_ct_l3proto_put(l3proto); | 762 | nf_ct_l3proto_put(l3proto); |
668 | nf_ct_extend_unregister(&nat_extend); | 763 | nf_ct_extend_unregister(&nat_extend); |
669 | rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL); | 764 | rcu_assign_pointer(nf_nat_seq_adjust_hook, NULL); |
765 | rcu_assign_pointer(nfnetlink_parse_nat_setup_hook, NULL); | ||
670 | synchronize_net(); | 766 | synchronize_net(); |
671 | } | 767 | } |
672 | 768 | ||
673 | MODULE_LICENSE("GPL"); | 769 | MODULE_LICENSE("GPL"); |
770 | MODULE_ALIAS("nf-nat-ipv4"); | ||
674 | 771 | ||
675 | module_init(nf_nat_init); | 772 | module_init(nf_nat_init); |
676 | module_exit(nf_nat_cleanup); | 773 | module_exit(nf_nat_cleanup); |
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 27de3c7b006e..622d7c671cb7 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c | |||
@@ -38,9 +38,16 @@ | |||
38 | #include <net/netfilter/nf_conntrack_core.h> | 38 | #include <net/netfilter/nf_conntrack_core.h> |
39 | #include <net/netfilter/nf_conntrack_extend.h> | 39 | #include <net/netfilter/nf_conntrack_extend.h> |
40 | #include <net/netfilter/nf_conntrack_acct.h> | 40 | #include <net/netfilter/nf_conntrack_acct.h> |
41 | #include <net/netfilter/nf_nat.h> | ||
41 | 42 | ||
42 | #define NF_CONNTRACK_VERSION "0.5.0" | 43 | #define NF_CONNTRACK_VERSION "0.5.0" |
43 | 44 | ||
45 | unsigned int | ||
46 | (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, | ||
47 | enum nf_nat_manip_type manip, | ||
48 | struct nlattr *attr) __read_mostly; | ||
49 | EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); | ||
50 | |||
44 | DEFINE_SPINLOCK(nf_conntrack_lock); | 51 | DEFINE_SPINLOCK(nf_conntrack_lock); |
45 | EXPORT_SYMBOL_GPL(nf_conntrack_lock); | 52 | EXPORT_SYMBOL_GPL(nf_conntrack_lock); |
46 | 53 | ||
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index cadfd15b44f6..08e82d64eb6f 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -689,71 +689,6 @@ ctnetlink_parse_tuple(struct nlattr *cda[], struct nf_conntrack_tuple *tuple, | |||
689 | return 0; | 689 | return 0; |
690 | } | 690 | } |
691 | 691 | ||
692 | #ifdef CONFIG_NF_NAT_NEEDED | ||
693 | static const struct nla_policy protonat_nla_policy[CTA_PROTONAT_MAX+1] = { | ||
694 | [CTA_PROTONAT_PORT_MIN] = { .type = NLA_U16 }, | ||
695 | [CTA_PROTONAT_PORT_MAX] = { .type = NLA_U16 }, | ||
696 | }; | ||
697 | |||
698 | static int nfnetlink_parse_nat_proto(struct nlattr *attr, | ||
699 | const struct nf_conn *ct, | ||
700 | struct nf_nat_range *range) | ||
701 | { | ||
702 | struct nlattr *tb[CTA_PROTONAT_MAX+1]; | ||
703 | const struct nf_nat_protocol *npt; | ||
704 | int err; | ||
705 | |||
706 | err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); | ||
707 | if (err < 0) | ||
708 | return err; | ||
709 | |||
710 | npt = nf_nat_proto_find_get(nf_ct_protonum(ct)); | ||
711 | if (npt->nlattr_to_range) | ||
712 | err = npt->nlattr_to_range(tb, range); | ||
713 | nf_nat_proto_put(npt); | ||
714 | return err; | ||
715 | } | ||
716 | |||
717 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { | ||
718 | [CTA_NAT_MINIP] = { .type = NLA_U32 }, | ||
719 | [CTA_NAT_MAXIP] = { .type = NLA_U32 }, | ||
720 | }; | ||
721 | |||
722 | static inline int | ||
723 | nfnetlink_parse_nat(struct nlattr *nat, | ||
724 | const struct nf_conn *ct, struct nf_nat_range *range) | ||
725 | { | ||
726 | struct nlattr *tb[CTA_NAT_MAX+1]; | ||
727 | int err; | ||
728 | |||
729 | memset(range, 0, sizeof(*range)); | ||
730 | |||
731 | err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); | ||
732 | if (err < 0) | ||
733 | return err; | ||
734 | |||
735 | if (tb[CTA_NAT_MINIP]) | ||
736 | range->min_ip = nla_get_be32(tb[CTA_NAT_MINIP]); | ||
737 | |||
738 | if (!tb[CTA_NAT_MAXIP]) | ||
739 | range->max_ip = range->min_ip; | ||
740 | else | ||
741 | range->max_ip = nla_get_be32(tb[CTA_NAT_MAXIP]); | ||
742 | |||
743 | if (range->min_ip) | ||
744 | range->flags |= IP_NAT_RANGE_MAP_IPS; | ||
745 | |||
746 | if (!tb[CTA_NAT_PROTO]) | ||
747 | return 0; | ||
748 | |||
749 | err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range); | ||
750 | if (err < 0) | ||
751 | return err; | ||
752 | |||
753 | return 0; | ||
754 | } | ||
755 | #endif | ||
756 | |||
757 | static inline int | 692 | static inline int |
758 | ctnetlink_parse_help(struct nlattr *attr, char **helper_name) | 693 | ctnetlink_parse_help(struct nlattr *attr, char **helper_name) |
759 | { | 694 | { |
@@ -879,6 +814,34 @@ out: | |||
879 | } | 814 | } |
880 | 815 | ||
881 | static int | 816 | static int |
817 | ctnetlink_parse_nat_setup(struct nf_conn *ct, | ||
818 | enum nf_nat_manip_type manip, | ||
819 | struct nlattr *attr) | ||
820 | { | ||
821 | typeof(nfnetlink_parse_nat_setup_hook) parse_nat_setup; | ||
822 | |||
823 | parse_nat_setup = rcu_dereference(nfnetlink_parse_nat_setup_hook); | ||
824 | if (!parse_nat_setup) { | ||
825 | #ifdef CONFIG_KMOD | ||
826 | rcu_read_unlock(); | ||
827 | nfnl_unlock(); | ||
828 | if (request_module("nf-nat-ipv4") < 0) { | ||
829 | nfnl_lock(); | ||
830 | rcu_read_lock(); | ||
831 | return -EOPNOTSUPP; | ||
832 | } | ||
833 | nfnl_lock(); | ||
834 | rcu_read_lock(); | ||
835 | if (nfnetlink_parse_nat_setup_hook) | ||
836 | return -EAGAIN; | ||
837 | #endif | ||
838 | return -EOPNOTSUPP; | ||
839 | } | ||
840 | |||
841 | return parse_nat_setup(ct, manip, attr); | ||
842 | } | ||
843 | |||
844 | static int | ||
882 | ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) | 845 | ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) |
883 | { | 846 | { |
884 | unsigned long d; | 847 | unsigned long d; |
@@ -897,31 +860,6 @@ ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) | |||
897 | /* ASSURED bit can only be set */ | 860 | /* ASSURED bit can only be set */ |
898 | return -EBUSY; | 861 | return -EBUSY; |
899 | 862 | ||
900 | if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) { | ||
901 | #ifndef CONFIG_NF_NAT_NEEDED | ||
902 | return -EOPNOTSUPP; | ||
903 | #else | ||
904 | struct nf_nat_range range; | ||
905 | |||
906 | if (cda[CTA_NAT_DST]) { | ||
907 | if (nfnetlink_parse_nat(cda[CTA_NAT_DST], ct, | ||
908 | &range) < 0) | ||
909 | return -EINVAL; | ||
910 | if (nf_nat_initialized(ct, IP_NAT_MANIP_DST)) | ||
911 | return -EEXIST; | ||
912 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); | ||
913 | } | ||
914 | if (cda[CTA_NAT_SRC]) { | ||
915 | if (nfnetlink_parse_nat(cda[CTA_NAT_SRC], ct, | ||
916 | &range) < 0) | ||
917 | return -EINVAL; | ||
918 | if (nf_nat_initialized(ct, IP_NAT_MANIP_SRC)) | ||
919 | return -EEXIST; | ||
920 | nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); | ||
921 | } | ||
922 | #endif | ||
923 | } | ||
924 | |||
925 | /* Be careful here, modifying NAT bits can screw up things, | 863 | /* Be careful here, modifying NAT bits can screw up things, |
926 | * so don't let users modify them directly if they don't pass | 864 | * so don't let users modify them directly if they don't pass |
927 | * nf_nat_range. */ | 865 | * nf_nat_range. */ |
@@ -929,6 +867,31 @@ ctnetlink_change_status(struct nf_conn *ct, struct nlattr *cda[]) | |||
929 | return 0; | 867 | return 0; |
930 | } | 868 | } |
931 | 869 | ||
870 | static int | ||
871 | ctnetlink_change_nat(struct nf_conn *ct, struct nlattr *cda[]) | ||
872 | { | ||
873 | #ifdef CONFIG_NF_NAT_NEEDED | ||
874 | int ret; | ||
875 | |||
876 | if (cda[CTA_NAT_DST]) { | ||
877 | ret = ctnetlink_parse_nat_setup(ct, | ||
878 | IP_NAT_MANIP_DST, | ||
879 | cda[CTA_NAT_DST]); | ||
880 | if (ret < 0) | ||
881 | return ret; | ||
882 | } | ||
883 | if (cda[CTA_NAT_SRC]) { | ||
884 | ret = ctnetlink_parse_nat_setup(ct, | ||
885 | IP_NAT_MANIP_SRC, | ||
886 | cda[CTA_NAT_SRC]); | ||
887 | if (ret < 0) | ||
888 | return ret; | ||
889 | } | ||
890 | return 0; | ||
891 | #else | ||
892 | return -EOPNOTSUPP; | ||
893 | #endif | ||
894 | } | ||
932 | 895 | ||
933 | static inline int | 896 | static inline int |
934 | ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) | 897 | ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) |
@@ -1157,6 +1120,14 @@ ctnetlink_create_conntrack(struct nlattr *cda[], | |||
1157 | } | 1120 | } |
1158 | } | 1121 | } |
1159 | 1122 | ||
1123 | if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) { | ||
1124 | err = ctnetlink_change_nat(ct, cda); | ||
1125 | if (err < 0) { | ||
1126 | rcu_read_unlock(); | ||
1127 | goto err; | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1160 | if (cda[CTA_PROTOINFO]) { | 1131 | if (cda[CTA_PROTOINFO]) { |
1161 | err = ctnetlink_change_protoinfo(ct, cda); | 1132 | err = ctnetlink_change_protoinfo(ct, cda); |
1162 | if (err < 0) { | 1133 | if (err < 0) { |
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index b75c9c4a995d..4739f9f961d8 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c | |||
@@ -44,15 +44,17 @@ static struct sock *nfnl = NULL; | |||
44 | static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]; | 44 | static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]; |
45 | static DEFINE_MUTEX(nfnl_mutex); | 45 | static DEFINE_MUTEX(nfnl_mutex); |
46 | 46 | ||
47 | static inline void nfnl_lock(void) | 47 | void nfnl_lock(void) |
48 | { | 48 | { |
49 | mutex_lock(&nfnl_mutex); | 49 | mutex_lock(&nfnl_mutex); |
50 | } | 50 | } |
51 | EXPORT_SYMBOL_GPL(nfnl_lock); | ||
51 | 52 | ||
52 | static inline void nfnl_unlock(void) | 53 | void nfnl_unlock(void) |
53 | { | 54 | { |
54 | mutex_unlock(&nfnl_mutex); | 55 | mutex_unlock(&nfnl_mutex); |
55 | } | 56 | } |
57 | EXPORT_SYMBOL_GPL(nfnl_unlock); | ||
56 | 58 | ||
57 | int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) | 59 | int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) |
58 | { | 60 | { |
@@ -132,6 +134,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
132 | return 0; | 134 | return 0; |
133 | 135 | ||
134 | type = nlh->nlmsg_type; | 136 | type = nlh->nlmsg_type; |
137 | replay: | ||
135 | ss = nfnetlink_get_subsys(type); | 138 | ss = nfnetlink_get_subsys(type); |
136 | if (!ss) { | 139 | if (!ss) { |
137 | #ifdef CONFIG_KMOD | 140 | #ifdef CONFIG_KMOD |
@@ -165,7 +168,10 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
165 | } else | 168 | } else |
166 | return -EINVAL; | 169 | return -EINVAL; |
167 | 170 | ||
168 | return nc->call(nfnl, skb, nlh, cda); | 171 | err = nc->call(nfnl, skb, nlh, cda); |
172 | if (err == -EAGAIN) | ||
173 | goto replay; | ||
174 | return err; | ||
169 | } | 175 | } |
170 | } | 176 | } |
171 | 177 | ||