aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/dccp.h40
-rw-r--r--net/dccp/dccp.h9
-rw-r--r--net/dccp/ipv4.c30
-rw-r--r--net/dccp/minisocks.c6
-rw-r--r--net/dccp/output.c14
-rw-r--r--net/dccp/proto.c85
6 files changed, 154 insertions, 30 deletions
diff --git a/include/linux/dccp.h b/include/linux/dccp.h
index 8bf4bacb5051..0e72708677e4 100644
--- a/include/linux/dccp.h
+++ b/include/linux/dccp.h
@@ -4,16 +4,6 @@
4#include <linux/types.h> 4#include <linux/types.h>
5#include <asm/byteorder.h> 5#include <asm/byteorder.h>
6 6
7/* Structure describing an Internet (DCCP) socket address. */
8struct sockaddr_dccp {
9 __u16 sdccp_family; /* Address family */
10 __u16 sdccp_port; /* Port number */
11 __u32 sdccp_addr; /* Internet address */
12 __u32 sdccp_service; /* Service */
13 /* Pad to size of `struct sockaddr': 16 bytes . */
14 __u32 sdccp_pad;
15};
16
17/** 7/**
18 * struct dccp_hdr - generic part of DCCP packet header 8 * struct dccp_hdr - generic part of DCCP packet header
19 * 9 *
@@ -188,6 +178,9 @@ enum {
188 178
189/* DCCP socket options */ 179/* DCCP socket options */
190#define DCCP_SOCKOPT_PACKET_SIZE 1 180#define DCCP_SOCKOPT_PACKET_SIZE 1
181#define DCCP_SOCKOPT_SERVICE 2
182
183#define DCCP_SERVICE_LIST_MAX_LEN 32
191 184
192#ifdef __KERNEL__ 185#ifdef __KERNEL__
193 186
@@ -382,6 +375,25 @@ enum dccp_role {
382 DCCP_ROLE_SERVER, 375 DCCP_ROLE_SERVER,
383}; 376};
384 377
378struct dccp_service_list {
379 __u32 dccpsl_nr;
380 __u32 dccpsl_list[0];
381};
382
383#define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1)
384
385static inline int dccp_list_has_service(const struct dccp_service_list *sl,
386 const u32 service)
387{
388 if (likely(sl != NULL)) {
389 u32 i = sl->dccpsl_nr;
390 while (i--)
391 if (sl->dccpsl_list[i] == service)
392 return 1;
393 }
394 return 0;
395}
396
385/** 397/**
386 * struct dccp_sock - DCCP socket state 398 * struct dccp_sock - DCCP socket state
387 * 399 *
@@ -417,7 +429,8 @@ struct dccp_sock {
417 __u64 dccps_gss; 429 __u64 dccps_gss;
418 __u64 dccps_gsr; 430 __u64 dccps_gsr;
419 __u64 dccps_gar; 431 __u64 dccps_gar;
420 unsigned long dccps_service; 432 __u32 dccps_service;
433 struct dccp_service_list *dccps_service_list;
421 struct timeval dccps_timestamp_time; 434 struct timeval dccps_timestamp_time;
422 __u32 dccps_timestamp_echo; 435 __u32 dccps_timestamp_echo;
423 __u32 dccps_packet_size; 436 __u32 dccps_packet_size;
@@ -443,6 +456,11 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk)
443 return (struct dccp_sock *)sk; 456 return (struct dccp_sock *)sk;
444} 457}
445 458
459static inline int dccp_service_not_initialized(const struct sock *sk)
460{
461 return dccp_sk(sk)->dccps_service == DCCP_SERVICE_INVALID_VALUE;
462}
463
446static inline const char *dccp_role(const struct sock *sk) 464static inline const char *dccp_role(const struct sock *sk)
447{ 465{
448 switch (dccp_sk(sk)->dccps_role) { 466 switch (dccp_sk(sk)->dccps_role) {
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 95c4630b3b18..be7a660b6b24 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -258,13 +258,12 @@ extern int dccp_v4_send_reset(struct sock *sk,
258extern void dccp_send_close(struct sock *sk, const int active); 258extern void dccp_send_close(struct sock *sk, const int active);
259 259
260struct dccp_skb_cb { 260struct dccp_skb_cb {
261 __u8 dccpd_type; 261 __u8 dccpd_type:4;
262 __u8 dccpd_reset_code; 262 __u8 dccpd_ccval:4;
263 __u8 dccpd_service; 263 __u8 dccpd_reset_code;
264 __u8 dccpd_ccval; 264 __u16 dccpd_opt_len;
265 __u64 dccpd_seq; 265 __u64 dccpd_seq;
266 __u64 dccpd_ack_seq; 266 __u64 dccpd_ack_seq;
267 int dccpd_opt_len;
268}; 267};
269 268
270#define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0])) 269#define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0]))
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index e09907d8b7da..94a440b2685b 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -246,6 +246,9 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
246 246
247 dp->dccps_role = DCCP_ROLE_CLIENT; 247 dp->dccps_role = DCCP_ROLE_CLIENT;
248 248
249 if (dccp_service_not_initialized(sk))
250 return -EPROTO;
251
249 if (addr_len < sizeof(struct sockaddr_in)) 252 if (addr_len < sizeof(struct sockaddr_in))
250 return -EINVAL; 253 return -EINVAL;
251 254
@@ -661,6 +664,16 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk,
661 dccp_hdr(skb)->dccph_sport); 664 dccp_hdr(skb)->dccph_sport);
662} 665}
663 666
667static inline int dccp_bad_service_code(const struct sock *sk,
668 const __u32 service)
669{
670 const struct dccp_sock *dp = dccp_sk(sk);
671
672 if (dp->dccps_service == service)
673 return 0;
674 return !dccp_list_has_service(dp->dccps_service_list, service);
675}
676
664int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 677int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
665{ 678{
666 struct inet_request_sock *ireq; 679 struct inet_request_sock *ireq;
@@ -669,6 +682,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
669 struct dccp_request_sock *dreq; 682 struct dccp_request_sock *dreq;
670 const __u32 saddr = skb->nh.iph->saddr; 683 const __u32 saddr = skb->nh.iph->saddr;
671 const __u32 daddr = skb->nh.iph->daddr; 684 const __u32 daddr = skb->nh.iph->daddr;
685 const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
672 struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); 686 struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
673 __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY; 687 __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
674 struct dst_entry *dst = NULL; 688 struct dst_entry *dst = NULL;
@@ -680,6 +694,10 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
680 goto drop; 694 goto drop;
681 } 695 }
682 696
697 if (dccp_bad_service_code(sk, service)) {
698 reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
699 goto drop;
700 }
683 /* 701 /*
684 * TW buckets are converted to open requests without 702 * TW buckets are converted to open requests without
685 * limitations, they conserve resources and peer is 703 * limitations, they conserve resources and peer is
@@ -722,9 +740,9 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
722 * dccp_create_openreq_child. 740 * dccp_create_openreq_child.
723 */ 741 */
724 dreq = dccp_rsk(req); 742 dreq = dccp_rsk(req);
725 dreq->dreq_isr = dcb->dccpd_seq; 743 dreq->dreq_isr = dcb->dccpd_seq;
726 dreq->dreq_iss = dccp_v4_init_sequence(sk, skb); 744 dreq->dreq_iss = dccp_v4_init_sequence(sk, skb);
727 dreq->dreq_service = dccp_hdr_request(skb)->dccph_req_service; 745 dreq->dreq_service = service;
728 746
729 if (dccp_v4_send_response(sk, req, dst)) 747 if (dccp_v4_send_response(sk, req, dst))
730 goto drop_and_free; 748 goto drop_and_free;
@@ -1284,6 +1302,7 @@ static int dccp_v4_init_sock(struct sock *sk)
1284 sk->sk_write_space = dccp_write_space; 1302 sk->sk_write_space = dccp_write_space;
1285 dp->dccps_mss_cache = 536; 1303 dp->dccps_mss_cache = 536;
1286 dp->dccps_role = DCCP_ROLE_UNDEFINED; 1304 dp->dccps_role = DCCP_ROLE_UNDEFINED;
1305 dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
1287 1306
1288 return 0; 1307 return 0;
1289} 1308}
@@ -1305,6 +1324,11 @@ static int dccp_v4_destroy_sock(struct sock *sk)
1305 if (inet_csk(sk)->icsk_bind_hash != NULL) 1324 if (inet_csk(sk)->icsk_bind_hash != NULL)
1306 inet_put_port(&dccp_hashinfo, sk); 1325 inet_put_port(&dccp_hashinfo, sk);
1307 1326
1327 if (dp->dccps_service_list != NULL) {
1328 kfree(dp->dccps_service_list);
1329 dp->dccps_service_list = NULL;
1330 }
1331
1308 ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk); 1332 ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk);
1309 ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk); 1333 ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk);
1310 dccp_ackpkts_free(dp->dccps_hc_rx_ackpkts); 1334 dccp_ackpkts_free(dp->dccps_hc_rx_ackpkts);
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 18461bc04cbe..933e10db1789 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -93,9 +93,11 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
93 struct inet_connection_sock *newicsk = inet_csk(sk); 93 struct inet_connection_sock *newicsk = inet_csk(sk);
94 struct dccp_sock *newdp = dccp_sk(newsk); 94 struct dccp_sock *newdp = dccp_sk(newsk);
95 95
96 newdp->dccps_role = DCCP_ROLE_SERVER;
96 newdp->dccps_hc_rx_ackpkts = NULL; 97 newdp->dccps_hc_rx_ackpkts = NULL;
97 newdp->dccps_role = DCCP_ROLE_SERVER; 98 newdp->dccps_service_list = NULL;
98 newicsk->icsk_rto = DCCP_TIMEOUT_INIT; 99 newdp->dccps_service = dreq->dreq_service;
100 newicsk->icsk_rto = DCCP_TIMEOUT_INIT;
99 do_gettimeofday(&newdp->dccps_epoch); 101 do_gettimeofday(&newdp->dccps_epoch);
100 102
101 if (newdp->dccps_options.dccpo_send_ack_vector) { 103 if (newdp->dccps_options.dccpo_send_ack_vector) {
diff --git a/net/dccp/output.c b/net/dccp/output.c
index ea6d0e91e511..156b1d29a156 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -85,7 +85,7 @@ int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
85 switch (dcb->dccpd_type) { 85 switch (dcb->dccpd_type) {
86 case DCCP_PKT_REQUEST: 86 case DCCP_PKT_REQUEST:
87 dccp_hdr_request(skb)->dccph_req_service = 87 dccp_hdr_request(skb)->dccph_req_service =
88 dcb->dccpd_service; 88 dp->dccps_service;
89 break; 89 break;
90 case DCCP_PKT_RESET: 90 case DCCP_PKT_RESET:
91 dccp_hdr_reset(skb)->dccph_reset_code = 91 dccp_hdr_reset(skb)->dccph_reset_code =
@@ -270,6 +270,7 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
270 struct request_sock *req) 270 struct request_sock *req)
271{ 271{
272 struct dccp_hdr *dh; 272 struct dccp_hdr *dh;
273 struct dccp_request_sock *dreq;
273 const int dccp_header_size = sizeof(struct dccp_hdr) + 274 const int dccp_header_size = sizeof(struct dccp_hdr) +
274 sizeof(struct dccp_hdr_ext) + 275 sizeof(struct dccp_hdr_ext) +
275 sizeof(struct dccp_hdr_response); 276 sizeof(struct dccp_hdr_response);
@@ -285,8 +286,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
285 skb->dst = dst_clone(dst); 286 skb->dst = dst_clone(dst);
286 skb->csum = 0; 287 skb->csum = 0;
287 288
289 dreq = dccp_rsk(req);
288 DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE; 290 DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE;
289 DCCP_SKB_CB(skb)->dccpd_seq = dccp_rsk(req)->dreq_iss; 291 DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss;
290 dccp_insert_options(sk, skb); 292 dccp_insert_options(sk, skb);
291 293
292 skb->h.raw = skb_push(skb, dccp_header_size); 294 skb->h.raw = skb_push(skb, dccp_header_size);
@@ -300,8 +302,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
300 DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; 302 DCCP_SKB_CB(skb)->dccpd_opt_len) / 4;
301 dh->dccph_type = DCCP_PKT_RESPONSE; 303 dh->dccph_type = DCCP_PKT_RESPONSE;
302 dh->dccph_x = 1; 304 dh->dccph_x = 1;
303 dccp_hdr_set_seq(dh, dccp_rsk(req)->dreq_iss); 305 dccp_hdr_set_seq(dh, dreq->dreq_iss);
304 dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dccp_rsk(req)->dreq_isr); 306 dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr);
307 dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service;
305 308
306 dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr, 309 dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr,
307 inet_rsk(req)->rmt_addr); 310 inet_rsk(req)->rmt_addr);
@@ -397,9 +400,6 @@ int dccp_connect(struct sock *sk)
397 skb_reserve(skb, MAX_DCCP_HEADER); 400 skb_reserve(skb, MAX_DCCP_HEADER);
398 401
399 DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST; 402 DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST;
400 /* FIXME: set service to something meaningful, coming
401 * from userspace*/
402 DCCP_SKB_CB(skb)->dccpd_service = 0;
403 skb->csum = 0; 403 skb->csum = 0;
404 skb_set_owner_w(skb, sk); 404 skb_set_owner_w(skb, sk);
405 405
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 18a0e69c9dc7..9bda2868eba6 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -94,7 +94,15 @@ EXPORT_SYMBOL_GPL(dccp_state_name);
94 94
95static inline int dccp_listen_start(struct sock *sk) 95static inline int dccp_listen_start(struct sock *sk)
96{ 96{
97 dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN; 97 struct dccp_sock *dp = dccp_sk(sk);
98
99 dp->dccps_role = DCCP_ROLE_LISTEN;
100 /*
101 * Apps need to use setsockopt(DCCP_SOCKOPT_SERVICE)
102 * before calling listen()
103 */
104 if (dccp_service_not_initialized(sk))
105 return -EPROTO;
98 return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE); 106 return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE);
99} 107}
100 108
@@ -202,6 +210,42 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
202 return -ENOIOCTLCMD; 210 return -ENOIOCTLCMD;
203} 211}
204 212
213static int dccp_setsockopt_service(struct sock *sk, const u32 service,
214 char __user *optval, int optlen)
215{
216 struct dccp_sock *dp = dccp_sk(sk);
217 struct dccp_service_list *sl = NULL;
218
219 if (service == DCCP_SERVICE_INVALID_VALUE ||
220 optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
221 return -EINVAL;
222
223 if (optlen > sizeof(service)) {
224 sl = kmalloc(optlen, GFP_KERNEL);
225 if (sl == NULL)
226 return -ENOMEM;
227
228 sl->dccpsl_nr = optlen / sizeof(u32) - 1;
229 if (copy_from_user(sl->dccpsl_list,
230 optval + sizeof(service),
231 optlen - sizeof(service)) ||
232 dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
233 kfree(sl);
234 return -EFAULT;
235 }
236 }
237
238 lock_sock(sk);
239 dp->dccps_service = service;
240
241 if (dp->dccps_service_list != NULL)
242 kfree(dp->dccps_service_list);
243
244 dp->dccps_service_list = sl;
245 release_sock(sk);
246 return 0;
247}
248
205int dccp_setsockopt(struct sock *sk, int level, int optname, 249int dccp_setsockopt(struct sock *sk, int level, int optname,
206 char __user *optval, int optlen) 250 char __user *optval, int optlen)
207{ 251{
@@ -218,8 +262,10 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
218 if (get_user(val, (int __user *)optval)) 262 if (get_user(val, (int __user *)optval))
219 return -EFAULT; 263 return -EFAULT;
220 264
221 lock_sock(sk); 265 if (optname == DCCP_SOCKOPT_SERVICE)
266 return dccp_setsockopt_service(sk, val, optval, optlen);
222 267
268 lock_sock(sk);
223 dp = dccp_sk(sk); 269 dp = dccp_sk(sk);
224 err = 0; 270 err = 0;
225 271
@@ -236,6 +282,37 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
236 return err; 282 return err;
237} 283}
238 284
285static int dccp_getsockopt_service(struct sock *sk, int len,
286 u32 __user *optval,
287 int __user *optlen)
288{
289 const struct dccp_sock *dp = dccp_sk(sk);
290 const struct dccp_service_list *sl;
291 int err = -ENOENT, slen = 0, total_len = sizeof(u32);
292
293 lock_sock(sk);
294 if (dccp_service_not_initialized(sk))
295 goto out;
296
297 if ((sl = dp->dccps_service_list) != NULL) {
298 slen = sl->dccpsl_nr * sizeof(u32);
299 total_len += slen;
300 }
301
302 err = -EINVAL;
303 if (total_len > len)
304 goto out;
305
306 err = 0;
307 if (put_user(total_len, optlen) ||
308 put_user(dp->dccps_service, optval) ||
309 (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
310 err = -EFAULT;
311out:
312 release_sock(sk);
313 return err;
314}
315
239int dccp_getsockopt(struct sock *sk, int level, int optname, 316int dccp_getsockopt(struct sock *sk, int level, int optname,
240 char __user *optval, int __user *optlen) 317 char __user *optval, int __user *optlen)
241{ 318{
@@ -248,6 +325,10 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
248 if (get_user(len, optlen)) 325 if (get_user(len, optlen))
249 return -EFAULT; 326 return -EFAULT;
250 327
328 if (optname == DCCP_SOCKOPT_SERVICE)
329 return dccp_getsockopt_service(sk, len,
330 (u32 __user *)optval, optlen);
331
251 len = min_t(unsigned int, len, sizeof(int)); 332 len = min_t(unsigned int, len, sizeof(int));
252 if (len < 0) 333 if (len < 0)
253 return -EINVAL; 334 return -EINVAL;