aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/dccp.h2
-rw-r--r--net/dccp/ccid.h31
-rw-r--r--net/dccp/ccids/ccid3.c56
-rw-r--r--net/dccp/proto.c17
4 files changed, 100 insertions, 6 deletions
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 13f9b78483fc..71fab4311e92 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -179,6 +179,8 @@ enum {
179/* DCCP socket options */ 179/* DCCP socket options */
180#define DCCP_SOCKOPT_PACKET_SIZE 1 180#define DCCP_SOCKOPT_PACKET_SIZE 1
181#define DCCP_SOCKOPT_SERVICE 2 181#define DCCP_SOCKOPT_SERVICE 2
182#define DCCP_SOCKOPT_CCID_RX_INFO 128
183#define DCCP_SOCKOPT_CCID_TX_INFO 192
182 184
183#define DCCP_SERVICE_LIST_MAX_LEN 32 185#define DCCP_SERVICE_LIST_MAX_LEN 32
184 186
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index 962f1e9e2f7e..21e55142dcd3 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -14,6 +14,7 @@
14 */ 14 */
15 15
16#include <net/sock.h> 16#include <net/sock.h>
17#include <linux/compiler.h>
17#include <linux/dccp.h> 18#include <linux/dccp.h>
18#include <linux/list.h> 19#include <linux/list.h>
19#include <linux/module.h> 20#include <linux/module.h>
@@ -54,6 +55,14 @@ struct ccid {
54 struct tcp_info *info); 55 struct tcp_info *info);
55 void (*ccid_hc_tx_get_info)(struct sock *sk, 56 void (*ccid_hc_tx_get_info)(struct sock *sk,
56 struct tcp_info *info); 57 struct tcp_info *info);
58 int (*ccid_hc_rx_getsockopt)(struct sock *sk,
59 const int optname, int len,
60 u32 __user *optval,
61 int __user *optlen);
62 int (*ccid_hc_tx_getsockopt)(struct sock *sk,
63 const int optname, int len,
64 u32 __user *optval,
65 int __user *optlen);
57}; 66};
58 67
59extern int ccid_register(struct ccid *ccid); 68extern int ccid_register(struct ccid *ccid);
@@ -177,4 +186,26 @@ static inline void ccid_hc_tx_get_info(struct ccid *ccid, struct sock *sk,
177 if (ccid->ccid_hc_tx_get_info != NULL) 186 if (ccid->ccid_hc_tx_get_info != NULL)
178 ccid->ccid_hc_tx_get_info(sk, info); 187 ccid->ccid_hc_tx_get_info(sk, info);
179} 188}
189
190static inline int ccid_hc_rx_getsockopt(struct ccid *ccid, struct sock *sk,
191 const int optname, int len,
192 u32 __user *optval, int __user *optlen)
193{
194 int rc = -ENOPROTOOPT;
195 if (ccid->ccid_hc_rx_getsockopt != NULL)
196 rc = ccid->ccid_hc_rx_getsockopt(sk, optname, len,
197 optval, optlen);
198 return rc;
199}
200
201static inline int ccid_hc_tx_getsockopt(struct ccid *ccid, struct sock *sk,
202 const int optname, int len,
203 u32 __user *optval, int __user *optlen)
204{
205 int rc = -ENOPROTOOPT;
206 if (ccid->ccid_hc_tx_getsockopt != NULL)
207 rc = ccid->ccid_hc_tx_getsockopt(sk, optname, len,
208 optval, optlen);
209 return rc;
210}
180#endif /* _CCID_H */ 211#endif /* _CCID_H */
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index 38aa84986118..aa68e0ab274d 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -1120,6 +1120,60 @@ static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info)
1120 info->tcpi_rtt = hctx->ccid3hctx_rtt; 1120 info->tcpi_rtt = hctx->ccid3hctx_rtt;
1121} 1121}
1122 1122
1123static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len,
1124 u32 __user *optval, int __user *optlen)
1125{
1126 const struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
1127 const void *val;
1128
1129 /* Listen socks doesn't have a private CCID block */
1130 if (sk->sk_state == DCCP_LISTEN)
1131 return -EINVAL;
1132
1133 switch (optname) {
1134 case DCCP_SOCKOPT_CCID_RX_INFO:
1135 if (len < sizeof(hcrx->ccid3hcrx_tfrc))
1136 return -EINVAL;
1137 len = sizeof(hcrx->ccid3hcrx_tfrc);
1138 val = &hcrx->ccid3hcrx_tfrc;
1139 break;
1140 default:
1141 return -ENOPROTOOPT;
1142 }
1143
1144 if (put_user(len, optlen) || copy_to_user(optval, val, len))
1145 return -EFAULT;
1146
1147 return 0;
1148}
1149
1150static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
1151 u32 __user *optval, int __user *optlen)
1152{
1153 const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
1154 const void *val;
1155
1156 /* Listen socks doesn't have a private CCID block */
1157 if (sk->sk_state == DCCP_LISTEN)
1158 return -EINVAL;
1159
1160 switch (optname) {
1161 case DCCP_SOCKOPT_CCID_TX_INFO:
1162 if (len < sizeof(hctx->ccid3hctx_tfrc))
1163 return -EINVAL;
1164 len = sizeof(hctx->ccid3hctx_tfrc);
1165 val = &hctx->ccid3hctx_tfrc;
1166 break;
1167 default:
1168 return -ENOPROTOOPT;
1169 }
1170
1171 if (put_user(len, optlen) || copy_to_user(optval, val, len))
1172 return -EFAULT;
1173
1174 return 0;
1175}
1176
1123static struct ccid ccid3 = { 1177static struct ccid ccid3 = {
1124 .ccid_id = 3, 1178 .ccid_id = 3,
1125 .ccid_name = "ccid3", 1179 .ccid_name = "ccid3",
@@ -1139,6 +1193,8 @@ static struct ccid ccid3 = {
1139 .ccid_hc_rx_packet_recv = ccid3_hc_rx_packet_recv, 1193 .ccid_hc_rx_packet_recv = ccid3_hc_rx_packet_recv,
1140 .ccid_hc_rx_get_info = ccid3_hc_rx_get_info, 1194 .ccid_hc_rx_get_info = ccid3_hc_rx_get_info,
1141 .ccid_hc_tx_get_info = ccid3_hc_tx_get_info, 1195 .ccid_hc_tx_get_info = ccid3_hc_tx_get_info,
1196 .ccid_hc_rx_getsockopt = ccid3_hc_rx_getsockopt,
1197 .ccid_hc_tx_getsockopt = ccid3_hc_tx_getsockopt,
1142}; 1198};
1143 1199
1144module_param(ccid3_debug, int, 0444); 1200module_param(ccid3_debug, int, 0444);
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 9bda2868eba6..a1cfd0e9e3bc 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -325,12 +325,7 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
325 if (get_user(len, optlen)) 325 if (get_user(len, optlen))
326 return -EFAULT; 326 return -EFAULT;
327 327
328 if (optname == DCCP_SOCKOPT_SERVICE) 328 if (len < sizeof(int))
329 return dccp_getsockopt_service(sk, len,
330 (u32 __user *)optval, optlen);
331
332 len = min_t(unsigned int, len, sizeof(int));
333 if (len < 0)
334 return -EINVAL; 329 return -EINVAL;
335 330
336 dp = dccp_sk(sk); 331 dp = dccp_sk(sk);
@@ -338,7 +333,17 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
338 switch (optname) { 333 switch (optname) {
339 case DCCP_SOCKOPT_PACKET_SIZE: 334 case DCCP_SOCKOPT_PACKET_SIZE:
340 val = dp->dccps_packet_size; 335 val = dp->dccps_packet_size;
336 len = sizeof(dp->dccps_packet_size);
341 break; 337 break;
338 case DCCP_SOCKOPT_SERVICE:
339 return dccp_getsockopt_service(sk, len,
340 (u32 __user *)optval, optlen);
341 case 128 ... 191:
342 return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
343 len, (u32 __user *)optval, optlen);
344 case 192 ... 255:
345 return ccid_hc_tx_getsockopt(dp->dccps_hc_tx_ccid, sk, optname,
346 len, (u32 __user *)optval, optlen);
342 default: 347 default:
343 return -ENOPROTOOPT; 348 return -ENOPROTOOPT;
344 } 349 }