diff options
author | Andy Adamson <andros@netapp.com> | 2009-04-03 01:28:01 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2009-04-03 20:41:15 -0400 |
commit | 0733d21338747483985a5964e852af160d88e429 (patch) | |
tree | fb8014cc66d3bfc33e1b7cc6a0da369bccc34cd3 | |
parent | 069b6ad4bb20abf175ea7875e82e8002154773af (diff) |
nfsd41: exchange_id operation
Implement the exchange_id operation confoming to
http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-28
Based on the client provided name, hash a client id.
If a confirmed one is found, compare the op's creds and
verifier. If the creds match and the verifier is different
then expire the old client (client re-incarnated), otherwise,
if both match, assume it's a replay and ignore it.
If an unconfirmed client is found, then copy the new creds
and verifer if need update, otherwise assume replay.
The client is moved to a confirmed state on create_session.
In the nfs41 branch set the exchange_id flags to
EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_SUPP_MOVED_REFER
(pNFS is not supported, Referrals are supported,
Migration is not.).
Address various scenarios from section 18.35 of the spec:
1. Check for EXCHGID4_FLAG_UPD_CONFIRMED_REC_A and set
EXCHGID4_FLAG_CONFIRMED_R as appropriate.
2. Return error codes per 18.35.4 scenarios.
3. Update client records or generate new client ids depending on
scenario.
Note: 18.35.4 case 3 probably still needs revisiting. The handling
seems not quite right.
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Andy Adamosn <andros@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfsd41: use utsname for major_id (and copy to server_scope)]
[nfsd41: fix handling of various exchange id scenarios]
Signed-off-by: Mike Sager <sager@netapp.com>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
[nfsd41: reverse use of EXCHGID4_INVAL_FLAG_MASK_A]
[simplify nfsd4_encode_exchange_id error handling]
[nfsd41: embed an xdr_netobj in nfsd4_exchange_id]
[nfsd41: return nfserr_serverfault for spa_how == SP4_MACH_CRED]
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r-- | fs/nfsd/nfs4state.c | 142 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 147 | ||||
-rw-r--r-- | include/linux/nfsd/state.h | 2 | ||||
-rw-r--r-- | include/linux/nfsd/xdr4.h | 7 |
4 files changed, 292 insertions, 6 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cfc01f415d15..4f963e902972 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -832,12 +832,152 @@ out_err: | |||
832 | return; | 832 | return; |
833 | } | 833 | } |
834 | 834 | ||
835 | /* | ||
836 | * Set the exchange_id flags returned by the server. | ||
837 | */ | ||
838 | static void | ||
839 | nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid) | ||
840 | { | ||
841 | /* pNFS is not supported */ | ||
842 | new->cl_exchange_flags |= EXCHGID4_FLAG_USE_NON_PNFS; | ||
843 | |||
844 | /* Referrals are supported, Migration is not. */ | ||
845 | new->cl_exchange_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; | ||
846 | |||
847 | /* set the wire flags to return to client. */ | ||
848 | clid->flags = new->cl_exchange_flags; | ||
849 | } | ||
850 | |||
835 | __be32 | 851 | __be32 |
836 | nfsd4_exchange_id(struct svc_rqst *rqstp, | 852 | nfsd4_exchange_id(struct svc_rqst *rqstp, |
837 | struct nfsd4_compound_state *cstate, | 853 | struct nfsd4_compound_state *cstate, |
838 | struct nfsd4_exchange_id *exid) | 854 | struct nfsd4_exchange_id *exid) |
839 | { | 855 | { |
840 | return -1; /* stub */ | 856 | struct nfs4_client *unconf, *conf, *new; |
857 | int status; | ||
858 | unsigned int strhashval; | ||
859 | char dname[HEXDIR_LEN]; | ||
860 | nfs4_verifier verf = exid->verifier; | ||
861 | u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr; | ||
862 | |||
863 | dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " | ||
864 | " ip_addr=%u flags %x, spa_how %d\n", | ||
865 | __func__, rqstp, exid, exid->clname.len, exid->clname.data, | ||
866 | ip_addr, exid->flags, exid->spa_how); | ||
867 | |||
868 | if (!check_name(exid->clname) || (exid->flags & ~EXCHGID4_FLAG_MASK_A)) | ||
869 | return nfserr_inval; | ||
870 | |||
871 | /* Currently only support SP4_NONE */ | ||
872 | switch (exid->spa_how) { | ||
873 | case SP4_NONE: | ||
874 | break; | ||
875 | case SP4_SSV: | ||
876 | return nfserr_encr_alg_unsupp; | ||
877 | default: | ||
878 | BUG(); /* checked by xdr code */ | ||
879 | case SP4_MACH_CRED: | ||
880 | return nfserr_serverfault; /* no excuse :-/ */ | ||
881 | } | ||
882 | |||
883 | status = nfs4_make_rec_clidname(dname, &exid->clname); | ||
884 | |||
885 | if (status) | ||
886 | goto error; | ||
887 | |||
888 | strhashval = clientstr_hashval(dname); | ||
889 | |||
890 | nfs4_lock_state(); | ||
891 | status = nfs_ok; | ||
892 | |||
893 | conf = find_confirmed_client_by_str(dname, strhashval); | ||
894 | if (conf) { | ||
895 | if (!same_verf(&verf, &conf->cl_verifier)) { | ||
896 | /* 18.35.4 case 8 */ | ||
897 | if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { | ||
898 | status = nfserr_not_same; | ||
899 | goto out; | ||
900 | } | ||
901 | /* Client reboot: destroy old state */ | ||
902 | expire_client(conf); | ||
903 | goto out_new; | ||
904 | } | ||
905 | if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { | ||
906 | /* 18.35.4 case 9 */ | ||
907 | if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { | ||
908 | status = nfserr_perm; | ||
909 | goto out; | ||
910 | } | ||
911 | expire_client(conf); | ||
912 | goto out_new; | ||
913 | } | ||
914 | if (ip_addr != conf->cl_addr && | ||
915 | !(exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A)) { | ||
916 | /* Client collision. 18.35.4 case 3 */ | ||
917 | status = nfserr_clid_inuse; | ||
918 | goto out; | ||
919 | } | ||
920 | /* | ||
921 | * Set bit when the owner id and verifier map to an already | ||
922 | * confirmed client id (18.35.3). | ||
923 | */ | ||
924 | exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; | ||
925 | |||
926 | /* | ||
927 | * Falling into 18.35.4 case 2, possible router replay. | ||
928 | * Leave confirmed record intact and return same result. | ||
929 | */ | ||
930 | copy_verf(conf, &verf); | ||
931 | new = conf; | ||
932 | goto out_copy; | ||
933 | } else { | ||
934 | /* 18.35.4 case 7 */ | ||
935 | if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { | ||
936 | status = nfserr_noent; | ||
937 | goto out; | ||
938 | } | ||
939 | } | ||
940 | |||
941 | unconf = find_unconfirmed_client_by_str(dname, strhashval); | ||
942 | if (unconf) { | ||
943 | /* | ||
944 | * Possible retry or client restart. Per 18.35.4 case 4, | ||
945 | * a new unconfirmed record should be generated regardless | ||
946 | * of whether any properties have changed. | ||
947 | */ | ||
948 | expire_client(unconf); | ||
949 | } | ||
950 | |||
951 | out_new: | ||
952 | /* Normal case */ | ||
953 | new = create_client(exid->clname, dname); | ||
954 | if (new == NULL) { | ||
955 | status = nfserr_resource; | ||
956 | goto out; | ||
957 | } | ||
958 | |||
959 | copy_verf(new, &verf); | ||
960 | copy_cred(&new->cl_cred, &rqstp->rq_cred); | ||
961 | new->cl_addr = ip_addr; | ||
962 | gen_clid(new); | ||
963 | gen_confirm(new); | ||
964 | add_to_unconfirmed(new, strhashval); | ||
965 | out_copy: | ||
966 | exid->clientid.cl_boot = new->cl_clientid.cl_boot; | ||
967 | exid->clientid.cl_id = new->cl_clientid.cl_id; | ||
968 | |||
969 | new->cl_seqid = exid->seqid = 1; | ||
970 | nfsd4_set_ex_flags(new, exid); | ||
971 | |||
972 | dprintk("nfsd4_exchange_id seqid %d flags %x\n", | ||
973 | new->cl_seqid, new->cl_exchange_flags); | ||
974 | status = nfs_ok; | ||
975 | |||
976 | out: | ||
977 | nfs4_unlock_state(); | ||
978 | error: | ||
979 | dprintk("nfsd4_exchange_id returns %d\n", ntohl(status)); | ||
980 | return status; | ||
841 | } | 981 | } |
842 | 982 | ||
843 | __be32 | 983 | __be32 |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 6973d61bedea..bebf6d249069 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <linux/fs.h> | 45 | #include <linux/fs.h> |
46 | #include <linux/namei.h> | 46 | #include <linux/namei.h> |
47 | #include <linux/vfs.h> | 47 | #include <linux/vfs.h> |
48 | #include <linux/utsname.h> | ||
48 | #include <linux/sunrpc/xdr.h> | 49 | #include <linux/sunrpc/xdr.h> |
49 | #include <linux/sunrpc/svc.h> | 50 | #include <linux/sunrpc/svc.h> |
50 | #include <linux/sunrpc/clnt.h> | 51 | #include <linux/sunrpc/clnt.h> |
@@ -998,9 +999,100 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel | |||
998 | 999 | ||
999 | static __be32 | 1000 | static __be32 |
1000 | nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, | 1001 | nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, |
1001 | struct nfsd4_exchange_id *clid) | 1002 | struct nfsd4_exchange_id *exid) |
1002 | { | 1003 | { |
1003 | return nfserr_opnotsupp; /* stub */ | 1004 | int dummy; |
1005 | DECODE_HEAD; | ||
1006 | |||
1007 | READ_BUF(NFS4_VERIFIER_SIZE); | ||
1008 | COPYMEM(exid->verifier.data, NFS4_VERIFIER_SIZE); | ||
1009 | |||
1010 | READ_BUF(4); | ||
1011 | READ32(exid->clname.len); | ||
1012 | |||
1013 | READ_BUF(exid->clname.len); | ||
1014 | SAVEMEM(exid->clname.data, exid->clname.len); | ||
1015 | |||
1016 | READ_BUF(4); | ||
1017 | READ32(exid->flags); | ||
1018 | |||
1019 | /* Ignore state_protect4_a */ | ||
1020 | READ_BUF(4); | ||
1021 | READ32(exid->spa_how); | ||
1022 | switch (exid->spa_how) { | ||
1023 | case SP4_NONE: | ||
1024 | break; | ||
1025 | case SP4_MACH_CRED: | ||
1026 | /* spo_must_enforce */ | ||
1027 | READ_BUF(4); | ||
1028 | READ32(dummy); | ||
1029 | READ_BUF(dummy * 4); | ||
1030 | p += dummy; | ||
1031 | |||
1032 | /* spo_must_allow */ | ||
1033 | READ_BUF(4); | ||
1034 | READ32(dummy); | ||
1035 | READ_BUF(dummy * 4); | ||
1036 | p += dummy; | ||
1037 | break; | ||
1038 | case SP4_SSV: | ||
1039 | /* ssp_ops */ | ||
1040 | READ_BUF(4); | ||
1041 | READ32(dummy); | ||
1042 | READ_BUF(dummy * 4); | ||
1043 | p += dummy; | ||
1044 | |||
1045 | READ_BUF(4); | ||
1046 | READ32(dummy); | ||
1047 | READ_BUF(dummy * 4); | ||
1048 | p += dummy; | ||
1049 | |||
1050 | /* ssp_hash_algs<> */ | ||
1051 | READ_BUF(4); | ||
1052 | READ32(dummy); | ||
1053 | READ_BUF(dummy); | ||
1054 | p += XDR_QUADLEN(dummy); | ||
1055 | |||
1056 | /* ssp_encr_algs<> */ | ||
1057 | READ_BUF(4); | ||
1058 | READ32(dummy); | ||
1059 | READ_BUF(dummy); | ||
1060 | p += XDR_QUADLEN(dummy); | ||
1061 | |||
1062 | /* ssp_window and ssp_num_gss_handles */ | ||
1063 | READ_BUF(8); | ||
1064 | READ32(dummy); | ||
1065 | READ32(dummy); | ||
1066 | break; | ||
1067 | default: | ||
1068 | goto xdr_error; | ||
1069 | } | ||
1070 | |||
1071 | /* Ignore Implementation ID */ | ||
1072 | READ_BUF(4); /* nfs_impl_id4 array length */ | ||
1073 | READ32(dummy); | ||
1074 | |||
1075 | if (dummy > 1) | ||
1076 | goto xdr_error; | ||
1077 | |||
1078 | if (dummy == 1) { | ||
1079 | /* nii_domain */ | ||
1080 | READ_BUF(4); | ||
1081 | READ32(dummy); | ||
1082 | READ_BUF(dummy); | ||
1083 | p += XDR_QUADLEN(dummy); | ||
1084 | |||
1085 | /* nii_name */ | ||
1086 | READ_BUF(4); | ||
1087 | READ32(dummy); | ||
1088 | READ_BUF(dummy); | ||
1089 | p += XDR_QUADLEN(dummy); | ||
1090 | |||
1091 | /* nii_date */ | ||
1092 | READ_BUF(12); | ||
1093 | p += 3; | ||
1094 | } | ||
1095 | DECODE_TAIL; | ||
1004 | } | 1096 | } |
1005 | 1097 | ||
1006 | static __be32 | 1098 | static __be32 |
@@ -2665,8 +2757,55 @@ static __be32 | |||
2665 | nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr, | 2757 | nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr, |
2666 | struct nfsd4_exchange_id *exid) | 2758 | struct nfsd4_exchange_id *exid) |
2667 | { | 2759 | { |
2668 | /* stub */ | 2760 | ENCODE_HEAD; |
2669 | return nfserr; | 2761 | char *major_id; |
2762 | char *server_scope; | ||
2763 | int major_id_sz; | ||
2764 | int server_scope_sz; | ||
2765 | uint64_t minor_id = 0; | ||
2766 | |||
2767 | if (nfserr) | ||
2768 | return nfserr; | ||
2769 | |||
2770 | major_id = utsname()->nodename; | ||
2771 | major_id_sz = strlen(major_id); | ||
2772 | server_scope = utsname()->nodename; | ||
2773 | server_scope_sz = strlen(server_scope); | ||
2774 | |||
2775 | RESERVE_SPACE( | ||
2776 | 8 /* eir_clientid */ + | ||
2777 | 4 /* eir_sequenceid */ + | ||
2778 | 4 /* eir_flags */ + | ||
2779 | 4 /* spr_how (SP4_NONE) */ + | ||
2780 | 8 /* so_minor_id */ + | ||
2781 | 4 /* so_major_id.len */ + | ||
2782 | (XDR_QUADLEN(major_id_sz) * 4) + | ||
2783 | 4 /* eir_server_scope.len */ + | ||
2784 | (XDR_QUADLEN(server_scope_sz) * 4) + | ||
2785 | 4 /* eir_server_impl_id.count (0) */); | ||
2786 | |||
2787 | WRITEMEM(&exid->clientid, 8); | ||
2788 | WRITE32(exid->seqid); | ||
2789 | WRITE32(exid->flags); | ||
2790 | |||
2791 | /* state_protect4_r. Currently only support SP4_NONE */ | ||
2792 | BUG_ON(exid->spa_how != SP4_NONE); | ||
2793 | WRITE32(exid->spa_how); | ||
2794 | |||
2795 | /* The server_owner struct */ | ||
2796 | WRITE64(minor_id); /* Minor id */ | ||
2797 | /* major id */ | ||
2798 | WRITE32(major_id_sz); | ||
2799 | WRITEMEM(major_id, major_id_sz); | ||
2800 | |||
2801 | /* Server scope */ | ||
2802 | WRITE32(server_scope_sz); | ||
2803 | WRITEMEM(server_scope, server_scope_sz); | ||
2804 | |||
2805 | /* Implementation id */ | ||
2806 | WRITE32(0); /* zero length nfs_impl_id4 array */ | ||
2807 | ADJUST_ARGS(); | ||
2808 | return 0; | ||
2670 | } | 2809 | } |
2671 | 2810 | ||
2672 | static __be32 | 2811 | static __be32 |
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 5b3a6660f3af..8d0b10167937 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h | |||
@@ -173,6 +173,8 @@ struct nfs4_client { | |||
173 | 173 | ||
174 | /* for nfs41 */ | 174 | /* for nfs41 */ |
175 | struct list_head cl_sessions; | 175 | struct list_head cl_sessions; |
176 | u32 cl_seqid; /* seqid for create_session */ | ||
177 | u32 cl_exchange_flags; | ||
176 | }; | 178 | }; |
177 | 179 | ||
178 | /* struct nfs4_client_reset | 180 | /* struct nfs4_client_reset |
diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 294940c58094..33ee71e988b4 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h | |||
@@ -345,7 +345,12 @@ struct nfsd4_write { | |||
345 | }; | 345 | }; |
346 | 346 | ||
347 | struct nfsd4_exchange_id { | 347 | struct nfsd4_exchange_id { |
348 | int foo; /* stub */ | 348 | nfs4_verifier verifier; |
349 | struct xdr_netobj clname; | ||
350 | u32 flags; | ||
351 | clientid_t clientid; | ||
352 | u32 seqid; | ||
353 | int spa_how; | ||
349 | }; | 354 | }; |
350 | 355 | ||
351 | struct nfsd4_create_session { | 356 | struct nfsd4_create_session { |