diff options
-rw-r--r-- | net/dccp/feat.c | 504 | ||||
-rw-r--r-- | net/dccp/feat.h | 12 |
2 files changed, 11 insertions, 505 deletions
diff --git a/net/dccp/feat.c b/net/dccp/feat.c index b127189550f1..077f78d579c4 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c | |||
@@ -1,8 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * net/dccp/feat.c | 2 | * net/dccp/feat.c |
3 | * | 3 | * |
4 | * An implementation of the DCCP protocol | 4 | * Feature negotiation for the DCCP protocol (RFC 4340, section 6) |
5 | * Andrea Bittau <a.bittau@cs.ucl.ac.uk> | 5 | * |
6 | * Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk> | ||
7 | * Rewrote from scratch, some bits from earlier code by | ||
8 | * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk> | ||
9 | * | ||
6 | * | 10 | * |
7 | * ASSUMPTIONS | 11 | * ASSUMPTIONS |
8 | * ----------- | 12 | * ----------- |
@@ -17,14 +21,10 @@ | |||
17 | * as published by the Free Software Foundation; either version | 21 | * as published by the Free Software Foundation; either version |
18 | * 2 of the License, or (at your option) any later version. | 22 | * 2 of the License, or (at your option) any later version. |
19 | */ | 23 | */ |
20 | |||
21 | #include <linux/module.h> | 24 | #include <linux/module.h> |
22 | |||
23 | #include "ccid.h" | 25 | #include "ccid.h" |
24 | #include "feat.h" | 26 | #include "feat.h" |
25 | 27 | ||
26 | #define DCCP_FEAT_SP_NOAGREE (-123) | ||
27 | |||
28 | /* | 28 | /* |
29 | * Feature activation handlers. | 29 | * Feature activation handlers. |
30 | * | 30 | * |
@@ -811,51 +811,6 @@ int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq) | |||
811 | return 0; | 811 | return 0; |
812 | } | 812 | } |
813 | 813 | ||
814 | static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr) | ||
815 | { | ||
816 | struct dccp_sock *dp = dccp_sk(sk); | ||
817 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
818 | /* figure out if we are changing our CCID or the peer's */ | ||
819 | const int rx = type == DCCPO_CHANGE_R; | ||
820 | const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid; | ||
821 | struct ccid *new_ccid; | ||
822 | |||
823 | /* Check if nothing is being changed. */ | ||
824 | if (ccid_nr == new_ccid_nr) | ||
825 | return 0; | ||
826 | |||
827 | new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC); | ||
828 | if (new_ccid == NULL) | ||
829 | return -ENOMEM; | ||
830 | |||
831 | if (rx) { | ||
832 | ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk); | ||
833 | dp->dccps_hc_rx_ccid = new_ccid; | ||
834 | dmsk->dccpms_rx_ccid = new_ccid_nr; | ||
835 | } else { | ||
836 | ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk); | ||
837 | dp->dccps_hc_tx_ccid = new_ccid; | ||
838 | dmsk->dccpms_tx_ccid = new_ccid_nr; | ||
839 | } | ||
840 | |||
841 | return 0; | ||
842 | } | ||
843 | |||
844 | static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val) | ||
845 | { | ||
846 | dccp_feat_debug(type, feat, val); | ||
847 | |||
848 | switch (feat) { | ||
849 | case DCCPF_CCID: | ||
850 | return dccp_feat_update_ccid(sk, type, val); | ||
851 | default: | ||
852 | dccp_pr_debug("UNIMPLEMENTED: %s(%d, ...)\n", | ||
853 | dccp_feat_typename(type), feat); | ||
854 | break; | ||
855 | } | ||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | /* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */ | 814 | /* Select the first entry in @servlist that also occurs in @clilist (6.3.1) */ |
860 | static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen) | 815 | static int dccp_feat_preflist_match(u8 *servlist, u8 slen, u8 *clilist, u8 clen) |
861 | { | 816 | { |
@@ -925,453 +880,6 @@ static int dccp_feat_reconcile(dccp_feat_val *fv, u8 *arr, u8 len, | |||
925 | return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len); | 880 | return dccp_feat_prefer(rc, fv->sp.vec, fv->sp.len); |
926 | } | 881 | } |
927 | 882 | ||
928 | #ifdef __this_is_the_old_framework_and_will_be_removed_later_in_a_subsequent_patch | ||
929 | static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt, | ||
930 | u8 *rpref, u8 rlen) | ||
931 | { | ||
932 | struct dccp_sock *dp = dccp_sk(sk); | ||
933 | u8 *spref, slen, *res = NULL; | ||
934 | int i, j, rc, agree = 1; | ||
935 | |||
936 | BUG_ON(rpref == NULL); | ||
937 | |||
938 | /* check if we are the black sheep */ | ||
939 | if (dp->dccps_role == DCCP_ROLE_CLIENT) { | ||
940 | spref = rpref; | ||
941 | slen = rlen; | ||
942 | rpref = opt->dccpop_val; | ||
943 | rlen = opt->dccpop_len; | ||
944 | } else { | ||
945 | spref = opt->dccpop_val; | ||
946 | slen = opt->dccpop_len; | ||
947 | } | ||
948 | /* | ||
949 | * Now we have server preference list in spref and client preference in | ||
950 | * rpref | ||
951 | */ | ||
952 | BUG_ON(spref == NULL); | ||
953 | BUG_ON(rpref == NULL); | ||
954 | |||
955 | /* FIXME sanity check vals */ | ||
956 | |||
957 | /* Are values in any order? XXX Lame "algorithm" here */ | ||
958 | for (i = 0; i < slen; i++) { | ||
959 | for (j = 0; j < rlen; j++) { | ||
960 | if (spref[i] == rpref[j]) { | ||
961 | res = &spref[i]; | ||
962 | break; | ||
963 | } | ||
964 | } | ||
965 | if (res) | ||
966 | break; | ||
967 | } | ||
968 | |||
969 | /* we didn't agree on anything */ | ||
970 | if (res == NULL) { | ||
971 | /* confirm previous value */ | ||
972 | switch (opt->dccpop_feat) { | ||
973 | case DCCPF_CCID: | ||
974 | /* XXX did i get this right? =P */ | ||
975 | if (opt->dccpop_type == DCCPO_CHANGE_L) | ||
976 | res = &dccp_msk(sk)->dccpms_tx_ccid; | ||
977 | else | ||
978 | res = &dccp_msk(sk)->dccpms_rx_ccid; | ||
979 | break; | ||
980 | |||
981 | default: | ||
982 | DCCP_BUG("Fell through, feat=%d", opt->dccpop_feat); | ||
983 | /* XXX implement res */ | ||
984 | return -EFAULT; | ||
985 | } | ||
986 | |||
987 | dccp_pr_debug("Don't agree... reconfirming %d\n", *res); | ||
988 | agree = 0; /* this is used for mandatory options... */ | ||
989 | } | ||
990 | |||
991 | /* need to put result and our preference list */ | ||
992 | rlen = 1 + opt->dccpop_len; | ||
993 | rpref = kmalloc(rlen, GFP_ATOMIC); | ||
994 | if (rpref == NULL) | ||
995 | return -ENOMEM; | ||
996 | |||
997 | *rpref = *res; | ||
998 | memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len); | ||
999 | |||
1000 | /* put it in the "confirm queue" */ | ||
1001 | if (opt->dccpop_sc == NULL) { | ||
1002 | opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC); | ||
1003 | if (opt->dccpop_sc == NULL) { | ||
1004 | kfree(rpref); | ||
1005 | return -ENOMEM; | ||
1006 | } | ||
1007 | } else { | ||
1008 | /* recycle the confirm slot */ | ||
1009 | BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); | ||
1010 | kfree(opt->dccpop_sc->dccpoc_val); | ||
1011 | dccp_pr_debug("recycling confirm slot\n"); | ||
1012 | } | ||
1013 | memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc)); | ||
1014 | |||
1015 | opt->dccpop_sc->dccpoc_val = rpref; | ||
1016 | opt->dccpop_sc->dccpoc_len = rlen; | ||
1017 | |||
1018 | /* update the option on our side [we are about to send the confirm] */ | ||
1019 | rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res); | ||
1020 | if (rc) { | ||
1021 | kfree(opt->dccpop_sc->dccpoc_val); | ||
1022 | kfree(opt->dccpop_sc); | ||
1023 | opt->dccpop_sc = NULL; | ||
1024 | return rc; | ||
1025 | } | ||
1026 | |||
1027 | dccp_pr_debug("Will confirm %d\n", *rpref); | ||
1028 | |||
1029 | /* say we want to change to X but we just got a confirm X, suppress our | ||
1030 | * change | ||
1031 | */ | ||
1032 | if (!opt->dccpop_conf) { | ||
1033 | if (*opt->dccpop_val == *res) | ||
1034 | opt->dccpop_conf = 1; | ||
1035 | dccp_pr_debug("won't ask for change of same feature\n"); | ||
1036 | } | ||
1037 | |||
1038 | return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */ | ||
1039 | } | ||
1040 | |||
1041 | static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
1042 | { | ||
1043 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
1044 | struct dccp_opt_pend *opt; | ||
1045 | int rc = 1; | ||
1046 | u8 t; | ||
1047 | |||
1048 | /* | ||
1049 | * We received a CHANGE. We gotta match it against our own preference | ||
1050 | * list. If we got a CHANGE_R it means it's a change for us, so we need | ||
1051 | * to compare our CHANGE_L list. | ||
1052 | */ | ||
1053 | if (type == DCCPO_CHANGE_L) | ||
1054 | t = DCCPO_CHANGE_R; | ||
1055 | else | ||
1056 | t = DCCPO_CHANGE_L; | ||
1057 | |||
1058 | /* find our preference list for this feature */ | ||
1059 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | ||
1060 | if (opt->dccpop_type != t || opt->dccpop_feat != feature) | ||
1061 | continue; | ||
1062 | |||
1063 | /* find the winner from the two preference lists */ | ||
1064 | rc = dccp_feat_reconcile(sk, opt, val, len); | ||
1065 | break; | ||
1066 | } | ||
1067 | |||
1068 | /* We didn't deal with the change. This can happen if we have no | ||
1069 | * preference list for the feature. In fact, it just shouldn't | ||
1070 | * happen---if we understand a feature, we should have a preference list | ||
1071 | * with at least the default value. | ||
1072 | */ | ||
1073 | BUG_ON(rc == 1); | ||
1074 | |||
1075 | return rc; | ||
1076 | } | ||
1077 | |||
1078 | static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
1079 | { | ||
1080 | struct dccp_opt_pend *opt; | ||
1081 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
1082 | u8 *copy; | ||
1083 | int rc; | ||
1084 | |||
1085 | /* NN features must be Change L (sec. 6.3.2) */ | ||
1086 | if (type != DCCPO_CHANGE_L) { | ||
1087 | dccp_pr_debug("received %s for NN feature %d\n", | ||
1088 | dccp_feat_typename(type), feature); | ||
1089 | return -EFAULT; | ||
1090 | } | ||
1091 | |||
1092 | /* XXX sanity check opt val */ | ||
1093 | |||
1094 | /* copy option so we can confirm it */ | ||
1095 | opt = kzalloc(sizeof(*opt), GFP_ATOMIC); | ||
1096 | if (opt == NULL) | ||
1097 | return -ENOMEM; | ||
1098 | |||
1099 | copy = kmemdup(val, len, GFP_ATOMIC); | ||
1100 | if (copy == NULL) { | ||
1101 | kfree(opt); | ||
1102 | return -ENOMEM; | ||
1103 | } | ||
1104 | |||
1105 | opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */ | ||
1106 | opt->dccpop_feat = feature; | ||
1107 | opt->dccpop_val = copy; | ||
1108 | opt->dccpop_len = len; | ||
1109 | |||
1110 | /* change feature */ | ||
1111 | rc = dccp_feat_update(sk, type, feature, *val); | ||
1112 | if (rc) { | ||
1113 | kfree(opt->dccpop_val); | ||
1114 | kfree(opt); | ||
1115 | return rc; | ||
1116 | } | ||
1117 | |||
1118 | dccp_feat_debug(type, feature, *copy); | ||
1119 | |||
1120 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); | ||
1121 | |||
1122 | return 0; | ||
1123 | } | ||
1124 | |||
1125 | static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk, | ||
1126 | u8 type, u8 feature) | ||
1127 | { | ||
1128 | /* XXX check if other confirms for that are queued and recycle slot */ | ||
1129 | struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC); | ||
1130 | |||
1131 | if (opt == NULL) { | ||
1132 | /* XXX what do we do? Ignoring should be fine. It's a change | ||
1133 | * after all =P | ||
1134 | */ | ||
1135 | return; | ||
1136 | } | ||
1137 | |||
1138 | switch (type) { | ||
1139 | case DCCPO_CHANGE_L: | ||
1140 | opt->dccpop_type = DCCPO_CONFIRM_R; | ||
1141 | break; | ||
1142 | case DCCPO_CHANGE_R: | ||
1143 | opt->dccpop_type = DCCPO_CONFIRM_L; | ||
1144 | break; | ||
1145 | default: | ||
1146 | DCCP_WARN("invalid type %d\n", type); | ||
1147 | kfree(opt); | ||
1148 | return; | ||
1149 | } | ||
1150 | opt->dccpop_feat = feature; | ||
1151 | opt->dccpop_val = NULL; | ||
1152 | opt->dccpop_len = 0; | ||
1153 | |||
1154 | /* change feature */ | ||
1155 | dccp_pr_debug("Empty %s(%d)\n", dccp_feat_typename(type), feature); | ||
1156 | |||
1157 | list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf); | ||
1158 | } | ||
1159 | |||
1160 | static void dccp_feat_flush_confirm(struct sock *sk) | ||
1161 | { | ||
1162 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
1163 | /* Check if there is anything to confirm in the first place */ | ||
1164 | int yes = !list_empty(&dmsk->dccpms_conf); | ||
1165 | |||
1166 | if (!yes) { | ||
1167 | struct dccp_opt_pend *opt; | ||
1168 | |||
1169 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | ||
1170 | if (opt->dccpop_conf) { | ||
1171 | yes = 1; | ||
1172 | break; | ||
1173 | } | ||
1174 | } | ||
1175 | } | ||
1176 | |||
1177 | if (!yes) | ||
1178 | return; | ||
1179 | |||
1180 | /* OK there is something to confirm... */ | ||
1181 | /* XXX check if packet is in flight? Send delayed ack?? */ | ||
1182 | if (sk->sk_state == DCCP_OPEN) | ||
1183 | dccp_send_ack(sk); | ||
1184 | } | ||
1185 | |||
1186 | int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len) | ||
1187 | { | ||
1188 | int rc; | ||
1189 | |||
1190 | /* Ignore Change requests other than during connection setup */ | ||
1191 | if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING) | ||
1192 | return 0; | ||
1193 | dccp_feat_debug(type, feature, *val); | ||
1194 | |||
1195 | /* figure out if it's SP or NN feature */ | ||
1196 | switch (feature) { | ||
1197 | /* deal with SP features */ | ||
1198 | case DCCPF_CCID: | ||
1199 | /* XXX Obsoleted by next patch | ||
1200 | rc = dccp_feat_sp(sk, type, feature, val, len); */ | ||
1201 | break; | ||
1202 | |||
1203 | /* deal with NN features */ | ||
1204 | case DCCPF_ACK_RATIO: | ||
1205 | /* XXX Obsoleted by next patch | ||
1206 | rc = dccp_feat_nn(sk, type, feature, val, len); */ | ||
1207 | break; | ||
1208 | |||
1209 | /* XXX implement other features */ | ||
1210 | default: | ||
1211 | dccp_pr_debug("UNIMPLEMENTED: not handling %s(%d, ...)\n", | ||
1212 | dccp_feat_typename(type), feature); | ||
1213 | rc = -EFAULT; | ||
1214 | break; | ||
1215 | } | ||
1216 | |||
1217 | /* check if there were problems changing features */ | ||
1218 | if (rc) { | ||
1219 | /* If we don't agree on SP, we sent a confirm for old value. | ||
1220 | * However we propagate rc to caller in case option was | ||
1221 | * mandatory | ||
1222 | */ | ||
1223 | if (rc != DCCP_FEAT_SP_NOAGREE) | ||
1224 | dccp_feat_empty_confirm(dccp_msk(sk), type, feature); | ||
1225 | } | ||
1226 | |||
1227 | /* generate the confirm [if required] */ | ||
1228 | dccp_feat_flush_confirm(sk); | ||
1229 | |||
1230 | return rc; | ||
1231 | } | ||
1232 | |||
1233 | EXPORT_SYMBOL_GPL(dccp_feat_change_recv); | ||
1234 | |||
1235 | int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature, | ||
1236 | u8 *val, u8 len) | ||
1237 | { | ||
1238 | u8 t; | ||
1239 | struct dccp_opt_pend *opt; | ||
1240 | struct dccp_minisock *dmsk = dccp_msk(sk); | ||
1241 | int found = 0; | ||
1242 | int all_confirmed = 1; | ||
1243 | |||
1244 | /* Ignore Confirm options other than during connection setup */ | ||
1245 | if (sk->sk_state != DCCP_LISTEN && sk->sk_state != DCCP_REQUESTING) | ||
1246 | return 0; | ||
1247 | dccp_feat_debug(type, feature, *val); | ||
1248 | |||
1249 | /* locate our change request */ | ||
1250 | switch (type) { | ||
1251 | case DCCPO_CONFIRM_L: t = DCCPO_CHANGE_R; break; | ||
1252 | case DCCPO_CONFIRM_R: t = DCCPO_CHANGE_L; break; | ||
1253 | default: DCCP_WARN("invalid type %d\n", type); | ||
1254 | return 1; | ||
1255 | |||
1256 | } | ||
1257 | /* XXX sanity check feature value */ | ||
1258 | |||
1259 | list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) { | ||
1260 | if (!opt->dccpop_conf && opt->dccpop_type == t && | ||
1261 | opt->dccpop_feat == feature) { | ||
1262 | found = 1; | ||
1263 | dccp_pr_debug("feature %d found\n", opt->dccpop_feat); | ||
1264 | |||
1265 | /* XXX do sanity check */ | ||
1266 | |||
1267 | opt->dccpop_conf = 1; | ||
1268 | |||
1269 | /* We got a confirmation---change the option */ | ||
1270 | dccp_feat_update(sk, opt->dccpop_type, | ||
1271 | opt->dccpop_feat, *val); | ||
1272 | |||
1273 | /* XXX check the return value of dccp_feat_update */ | ||
1274 | break; | ||
1275 | } | ||
1276 | |||
1277 | if (!opt->dccpop_conf) | ||
1278 | all_confirmed = 0; | ||
1279 | } | ||
1280 | |||
1281 | if (!found) | ||
1282 | dccp_pr_debug("%s(%d, ...) never requested\n", | ||
1283 | dccp_feat_typename(type), feature); | ||
1284 | return 0; | ||
1285 | } | ||
1286 | |||
1287 | EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv); | ||
1288 | #endif /* (later) */ | ||
1289 | |||
1290 | void dccp_feat_clean(struct dccp_minisock *dmsk) | ||
1291 | { | ||
1292 | struct dccp_opt_pend *opt, *next; | ||
1293 | |||
1294 | list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending, | ||
1295 | dccpop_node) { | ||
1296 | BUG_ON(opt->dccpop_val == NULL); | ||
1297 | kfree(opt->dccpop_val); | ||
1298 | |||
1299 | if (opt->dccpop_sc != NULL) { | ||
1300 | BUG_ON(opt->dccpop_sc->dccpoc_val == NULL); | ||
1301 | kfree(opt->dccpop_sc->dccpoc_val); | ||
1302 | kfree(opt->dccpop_sc); | ||
1303 | } | ||
1304 | |||
1305 | kfree(opt); | ||
1306 | } | ||
1307 | INIT_LIST_HEAD(&dmsk->dccpms_pending); | ||
1308 | |||
1309 | list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) { | ||
1310 | BUG_ON(opt == NULL); | ||
1311 | if (opt->dccpop_val != NULL) | ||
1312 | kfree(opt->dccpop_val); | ||
1313 | kfree(opt); | ||
1314 | } | ||
1315 | INIT_LIST_HEAD(&dmsk->dccpms_conf); | ||
1316 | } | ||
1317 | |||
1318 | EXPORT_SYMBOL_GPL(dccp_feat_clean); | ||
1319 | |||
1320 | /* this is to be called only when a listening sock creates its child. It is | ||
1321 | * assumed by the function---the confirm is not duplicated, but rather it is | ||
1322 | * "passed on". | ||
1323 | */ | ||
1324 | int dccp_feat_clone(struct sock *oldsk, struct sock *newsk) | ||
1325 | { | ||
1326 | struct dccp_minisock *olddmsk = dccp_msk(oldsk); | ||
1327 | struct dccp_minisock *newdmsk = dccp_msk(newsk); | ||
1328 | struct dccp_opt_pend *opt; | ||
1329 | int rc = 0; | ||
1330 | |||
1331 | INIT_LIST_HEAD(&newdmsk->dccpms_pending); | ||
1332 | INIT_LIST_HEAD(&newdmsk->dccpms_conf); | ||
1333 | |||
1334 | list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) { | ||
1335 | struct dccp_opt_pend *newopt; | ||
1336 | /* copy the value of the option */ | ||
1337 | u8 *val = kmemdup(opt->dccpop_val, opt->dccpop_len, GFP_ATOMIC); | ||
1338 | |||
1339 | if (val == NULL) | ||
1340 | goto out_clean; | ||
1341 | |||
1342 | newopt = kmemdup(opt, sizeof(*newopt), GFP_ATOMIC); | ||
1343 | if (newopt == NULL) { | ||
1344 | kfree(val); | ||
1345 | goto out_clean; | ||
1346 | } | ||
1347 | |||
1348 | /* insert the option */ | ||
1349 | newopt->dccpop_val = val; | ||
1350 | list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending); | ||
1351 | |||
1352 | /* XXX what happens with backlogs and multiple connections at | ||
1353 | * once... | ||
1354 | */ | ||
1355 | /* the master socket no longer needs to worry about confirms */ | ||
1356 | opt->dccpop_sc = NULL; /* it's not a memleak---new socket has it */ | ||
1357 | |||
1358 | /* reset state for a new socket */ | ||
1359 | opt->dccpop_conf = 0; | ||
1360 | } | ||
1361 | |||
1362 | /* XXX not doing anything about the conf queue */ | ||
1363 | |||
1364 | out: | ||
1365 | return rc; | ||
1366 | |||
1367 | out_clean: | ||
1368 | dccp_feat_clean(newdmsk); | ||
1369 | rc = -ENOMEM; | ||
1370 | goto out; | ||
1371 | } | ||
1372 | |||
1373 | EXPORT_SYMBOL_GPL(dccp_feat_clone); | ||
1374 | |||
1375 | /** | 883 | /** |
1376 | * dccp_feat_change_recv - Process incoming ChangeL/R options | 884 | * dccp_feat_change_recv - Process incoming ChangeL/R options |
1377 | * @fn: feature-negotiation list to update | 885 | * @fn: feature-negotiation list to update |
diff --git a/net/dccp/feat.h b/net/dccp/feat.h index f749610ae383..9b46e2a7866e 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h | |||
@@ -3,14 +3,14 @@ | |||
3 | /* | 3 | /* |
4 | * net/dccp/feat.h | 4 | * net/dccp/feat.h |
5 | * | 5 | * |
6 | * An implementation of the DCCP protocol | 6 | * Feature negotiation for the DCCP protocol (RFC 4340, section 6) |
7 | * Copyright (c) 2008 Gerrit Renker <gerrit@erg.abdn.ac.uk> | ||
7 | * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk> | 8 | * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk> |
8 | * | 9 | * |
9 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License version 2 as | 11 | * under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
12 | */ | 13 | */ |
13 | |||
14 | #include <linux/types.h> | 14 | #include <linux/types.h> |
15 | #include "dccp.h" | 15 | #include "dccp.h" |
16 | 16 | ||
@@ -118,8 +118,6 @@ extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, | |||
118 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); | 118 | extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val); |
119 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, | 119 | extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, |
120 | u8 mand, u8 opt, u8 feat, u8 *val, u8 len); | 120 | u8 mand, u8 opt, u8 feat, u8 *val, u8 len); |
121 | extern void dccp_feat_clean(struct dccp_minisock *dmsk); | ||
122 | extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk); | ||
123 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); | 121 | extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); |
124 | extern int dccp_feat_init(struct sock *sk); | 122 | extern int dccp_feat_init(struct sock *sk); |
125 | 123 | ||