32
32
#include <net/netfilter/nf_conntrack_zones.h>
33
33
#include <linux/netfilter/nf_nat.h>
34
34
35
+ #include "nf_internals.h"
36
+
35
37
static spinlock_t nf_nat_locks [CONNTRACK_LOCKS ];
36
38
37
39
static DEFINE_MUTEX (nf_nat_proto_mutex );
38
40
static const struct nf_nat_l3proto __rcu * nf_nat_l3protos [NFPROTO_NUMPROTO ]
39
41
__read_mostly ;
40
42
static const struct nf_nat_l4proto __rcu * * nf_nat_l4protos [NFPROTO_NUMPROTO ]
41
43
__read_mostly ;
44
+ static unsigned int nat_net_id __read_mostly ;
42
45
43
46
static struct hlist_head * nf_nat_bysource __read_mostly ;
44
47
static unsigned int nf_nat_htable_size __read_mostly ;
45
48
static unsigned int nf_nat_hash_rnd __read_mostly ;
46
49
50
+ struct nf_nat_lookup_hook_priv {
51
+ struct nf_hook_entries __rcu * entries ;
52
+
53
+ struct rcu_head rcu_head ;
54
+ };
55
+
56
+ struct nf_nat_hooks_net {
57
+ struct nf_hook_ops * nat_hook_ops ;
58
+ unsigned int users ;
59
+ };
60
+
61
+ struct nat_net {
62
+ struct nf_nat_hooks_net nat_proto_net [NFPROTO_NUMPROTO ];
63
+ };
64
+
47
65
inline const struct nf_nat_l3proto *
48
66
__nf_nat_l3proto_find (u8 family )
49
67
{
@@ -871,6 +889,138 @@ static struct nf_ct_helper_expectfn follow_master_nat = {
871
889
.expectfn = nf_nat_follow_master ,
872
890
};
873
891
892
+ int nf_nat_register_fn (struct net * net , const struct nf_hook_ops * ops ,
893
+ const struct nf_hook_ops * orig_nat_ops , unsigned int ops_count )
894
+ {
895
+ struct nat_net * nat_net = net_generic (net , nat_net_id );
896
+ struct nf_nat_hooks_net * nat_proto_net ;
897
+ struct nf_nat_lookup_hook_priv * priv ;
898
+ unsigned int hooknum = ops -> hooknum ;
899
+ struct nf_hook_ops * nat_ops ;
900
+ int i , ret ;
901
+
902
+ if (WARN_ON_ONCE (ops -> pf >= ARRAY_SIZE (nat_net -> nat_proto_net )))
903
+ return - EINVAL ;
904
+
905
+ nat_proto_net = & nat_net -> nat_proto_net [ops -> pf ];
906
+
907
+ for (i = 0 ; i < ops_count ; i ++ ) {
908
+ if (WARN_ON (orig_nat_ops [i ].pf != ops -> pf ))
909
+ return - EINVAL ;
910
+ if (orig_nat_ops [i ].hooknum == hooknum ) {
911
+ hooknum = i ;
912
+ break ;
913
+ }
914
+ }
915
+
916
+ if (WARN_ON_ONCE (i == ops_count ))
917
+ return - EINVAL ;
918
+
919
+ mutex_lock (& nf_nat_proto_mutex );
920
+ if (!nat_proto_net -> nat_hook_ops ) {
921
+ WARN_ON (nat_proto_net -> users != 0 );
922
+
923
+ nat_ops = kmemdup (orig_nat_ops , sizeof (* orig_nat_ops ) * ops_count , GFP_KERNEL );
924
+ if (!nat_ops ) {
925
+ mutex_unlock (& nf_nat_proto_mutex );
926
+ return - ENOMEM ;
927
+ }
928
+
929
+ for (i = 0 ; i < ops_count ; i ++ ) {
930
+ priv = kzalloc (sizeof (* priv ), GFP_KERNEL );
931
+ if (priv ) {
932
+ nat_ops [i ].priv = priv ;
933
+ continue ;
934
+ }
935
+ mutex_unlock (& nf_nat_proto_mutex );
936
+ while (i )
937
+ kfree (nat_ops [-- i ].priv );
938
+ kfree (nat_ops );
939
+ return - ENOMEM ;
940
+ }
941
+
942
+ ret = nf_register_net_hooks (net , nat_ops , ops_count );
943
+ if (ret < 0 ) {
944
+ mutex_unlock (& nf_nat_proto_mutex );
945
+ for (i = 0 ; i < ops_count ; i ++ )
946
+ kfree (nat_ops [i ].priv );
947
+ kfree (nat_ops );
948
+ return ret ;
949
+ }
950
+
951
+ nat_proto_net -> nat_hook_ops = nat_ops ;
952
+ }
953
+
954
+ nat_ops = nat_proto_net -> nat_hook_ops ;
955
+ priv = nat_ops [hooknum ].priv ;
956
+ if (WARN_ON_ONCE (!priv )) {
957
+ mutex_unlock (& nf_nat_proto_mutex );
958
+ return - EOPNOTSUPP ;
959
+ }
960
+
961
+ ret = nf_hook_entries_insert_raw (& priv -> entries , ops );
962
+ if (ret == 0 )
963
+ nat_proto_net -> users ++ ;
964
+
965
+ mutex_unlock (& nf_nat_proto_mutex );
966
+ return ret ;
967
+ }
968
+ EXPORT_SYMBOL_GPL (nf_nat_register_fn );
969
+
970
+ void nf_nat_unregister_fn (struct net * net , const struct nf_hook_ops * ops ,
971
+ unsigned int ops_count )
972
+ {
973
+ struct nat_net * nat_net = net_generic (net , nat_net_id );
974
+ struct nf_nat_hooks_net * nat_proto_net ;
975
+ struct nf_nat_lookup_hook_priv * priv ;
976
+ struct nf_hook_ops * nat_ops ;
977
+ int hooknum = ops -> hooknum ;
978
+ int i ;
979
+
980
+ if (ops -> pf >= ARRAY_SIZE (nat_net -> nat_proto_net ))
981
+ return ;
982
+
983
+ nat_proto_net = & nat_net -> nat_proto_net [ops -> pf ];
984
+
985
+ mutex_lock (& nf_nat_proto_mutex );
986
+ if (WARN_ON (nat_proto_net -> users == 0 ))
987
+ goto unlock ;
988
+
989
+ nat_proto_net -> users -- ;
990
+
991
+ nat_ops = nat_proto_net -> nat_hook_ops ;
992
+ for (i = 0 ; i < ops_count ; i ++ ) {
993
+ if (nat_ops [i ].hooknum == hooknum ) {
994
+ hooknum = i ;
995
+ break ;
996
+ }
997
+ }
998
+ if (WARN_ON_ONCE (i == ops_count ))
999
+ goto unlock ;
1000
+ priv = nat_ops [hooknum ].priv ;
1001
+ nf_hook_entries_delete_raw (& priv -> entries , ops );
1002
+
1003
+ if (nat_proto_net -> users == 0 ) {
1004
+ nf_unregister_net_hooks (net , nat_ops , ops_count );
1005
+
1006
+ for (i = 0 ; i < ops_count ; i ++ ) {
1007
+ priv = nat_ops [i ].priv ;
1008
+ kfree_rcu (priv , rcu_head );
1009
+ }
1010
+
1011
+ nat_proto_net -> nat_hook_ops = NULL ;
1012
+ kfree (nat_ops );
1013
+ }
1014
+ unlock :
1015
+ mutex_unlock (& nf_nat_proto_mutex );
1016
+ }
1017
+ EXPORT_SYMBOL_GPL (nf_nat_unregister_fn );
1018
+
1019
+ static struct pernet_operations nat_net_ops = {
1020
+ .id = & nat_net_id ,
1021
+ .size = sizeof (struct nat_net ),
1022
+ };
1023
+
874
1024
static int __init nf_nat_init (void )
875
1025
{
876
1026
int ret , i ;
@@ -894,6 +1044,12 @@ static int __init nf_nat_init(void)
894
1044
for (i = 0 ; i < CONNTRACK_LOCKS ; i ++ )
895
1045
spin_lock_init (& nf_nat_locks [i ]);
896
1046
1047
+ ret = register_pernet_subsys (& nat_net_ops );
1048
+ if (ret < 0 ) {
1049
+ nf_ct_extend_unregister (& nat_extend );
1050
+ return ret ;
1051
+ }
1052
+
897
1053
nf_ct_helper_expectfn_register (& follow_master_nat );
898
1054
899
1055
BUG_ON (nfnetlink_parse_nat_setup_hook != NULL );
@@ -925,6 +1081,7 @@ static void __exit nf_nat_cleanup(void)
925
1081
kfree (nf_nat_l4protos [i ]);
926
1082
synchronize_net ();
927
1083
nf_ct_free_hashtable (nf_nat_bysource , nf_nat_htable_size );
1084
+ unregister_pernet_subsys (& nat_net_ops );
928
1085
}
929
1086
930
1087
MODULE_LICENSE ("GPL" );
0 commit comments