[DCCP]: Introduce DCCP_SOCKOPT_SERVICE
authorArnaldo Carvalho de Melo <acme@mandriva.com>
Fri, 16 Sep 2005 23:58:40 +0000 (16:58 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 16 Sep 2005 23:58:40 +0000 (16:58 -0700)
As discussed in the dccp@vger mailing list:

Now applications have to use setsockopt(DCCP_SOCKOPT_SERVICE, service[s]),
prior to calling listen() and connect().

An array of unsigned ints can be passed meaning that the listening sock accepts
connection requests for several services.

With this we can ditch struct sockaddr_dccp and use only sockaddr_in (and
sockaddr_in6 in the future).

Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/dccp.h
net/dccp/dccp.h
net/dccp/ipv4.c
net/dccp/minisocks.c
net/dccp/output.c
net/dccp/proto.c

index 8bf4bacb5051a2df3e8b71dde9c1c464512b851c..0e72708677e498de755f9949c51eb63d61bd1757 100644 (file)
@@ -4,16 +4,6 @@
 #include <linux/types.h>
 #include <asm/byteorder.h>
 
-/* Structure describing an Internet (DCCP) socket address. */
-struct sockaddr_dccp {
-       __u16   sdccp_family;   /* Address family   */
-       __u16   sdccp_port;     /* Port number      */
-       __u32   sdccp_addr;     /* Internet address */
-       __u32   sdccp_service;  /* Service          */
-       /* Pad to size of `struct sockaddr': 16 bytes . */
-       __u32   sdccp_pad;
-};
-
 /**
  * struct dccp_hdr - generic part of DCCP packet header
  *
@@ -188,6 +178,9 @@ enum {
 
 /* DCCP socket options */
 #define DCCP_SOCKOPT_PACKET_SIZE       1
+#define DCCP_SOCKOPT_SERVICE           2
+
+#define DCCP_SERVICE_LIST_MAX_LEN      32
 
 #ifdef __KERNEL__
 
@@ -382,6 +375,25 @@ enum dccp_role {
        DCCP_ROLE_SERVER,
 };
 
+struct dccp_service_list {
+       __u32   dccpsl_nr;
+       __u32   dccpsl_list[0];
+};
+
+#define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1)
+
+static inline int dccp_list_has_service(const struct dccp_service_list *sl,
+                                       const u32 service)
+{
+       if (likely(sl != NULL)) {
+               u32 i = sl->dccpsl_nr;
+               while (i--)
+                       if (sl->dccpsl_list[i] == service)
+                               return 1; 
+       }
+       return 0;
+}
+
 /**
  * struct dccp_sock - DCCP socket state
  *
@@ -417,7 +429,8 @@ struct dccp_sock {
        __u64                           dccps_gss;
        __u64                           dccps_gsr;
        __u64                           dccps_gar;
-       unsigned long                   dccps_service;
+       __u32                           dccps_service;
+       struct dccp_service_list        *dccps_service_list;
        struct timeval                  dccps_timestamp_time;
        __u32                           dccps_timestamp_echo;
        __u32                           dccps_packet_size;
@@ -443,6 +456,11 @@ static inline struct dccp_sock *dccp_sk(const struct sock *sk)
        return (struct dccp_sock *)sk;
 }
 
+static inline int dccp_service_not_initialized(const struct sock *sk)
+{
+       return dccp_sk(sk)->dccps_service == DCCP_SERVICE_INVALID_VALUE;
+}
+
 static inline const char *dccp_role(const struct sock *sk)
 {
        switch (dccp_sk(sk)->dccps_role) {
index 95c4630b3b18454260b34209da7e54ae326ab8a1..be7a660b6b24c5f27a4ed63b82ee26be046e3ad0 100644 (file)
@@ -258,13 +258,12 @@ extern int           dccp_v4_send_reset(struct sock *sk,
 extern void       dccp_send_close(struct sock *sk, const int active);
 
 struct dccp_skb_cb {
-       __u8 dccpd_type;
-       __u8 dccpd_reset_code;
-       __u8 dccpd_service;
-       __u8 dccpd_ccval;
+       __u8  dccpd_type:4;
+       __u8  dccpd_ccval:4;
+       __u8  dccpd_reset_code;
+       __u16 dccpd_opt_len;
        __u64 dccpd_seq;
        __u64 dccpd_ack_seq;
-       int  dccpd_opt_len;
 };
 
 #define DCCP_SKB_CB(__skb) ((struct dccp_skb_cb *)&((__skb)->cb[0]))
index e09907d8b7da25e0ee9d992fdda3dd493dfc3ed1..94a440b2685b2eac309c627d01456a2d878b678d 100644 (file)
@@ -246,6 +246,9 @@ static int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
 
        dp->dccps_role = DCCP_ROLE_CLIENT;
 
+       if (dccp_service_not_initialized(sk))
+               return -EPROTO;
+
        if (addr_len < sizeof(struct sockaddr_in))
                return -EINVAL;
 
@@ -661,6 +664,16 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk,
                                           dccp_hdr(skb)->dccph_sport);
 }
 
+static inline int dccp_bad_service_code(const struct sock *sk,
+                                       const __u32 service)
+{
+       const struct dccp_sock *dp = dccp_sk(sk);
+
+       if (dp->dccps_service == service)
+               return 0;
+       return !dccp_list_has_service(dp->dccps_service_list, service);
+}
+
 int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
 {
        struct inet_request_sock *ireq;
@@ -669,6 +682,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
        struct dccp_request_sock *dreq;
        const __u32 saddr = skb->nh.iph->saddr;
        const __u32 daddr = skb->nh.iph->daddr;
+       const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
        struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
        __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
        struct dst_entry *dst = NULL;
@@ -680,6 +694,10 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
                goto drop;
        }
 
+       if (dccp_bad_service_code(sk, service)) {
+               reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
+               goto drop;
+       }
        /*
         * TW buckets are converted to open requests without
         * limitations, they conserve resources and peer is
@@ -722,9 +740,9 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
         * dccp_create_openreq_child.
         */
        dreq = dccp_rsk(req);
-       dreq->dreq_isr = dcb->dccpd_seq;
-       dreq->dreq_iss = dccp_v4_init_sequence(sk, skb);
-       dreq->dreq_service = dccp_hdr_request(skb)->dccph_req_service;
+       dreq->dreq_isr     = dcb->dccpd_seq;
+       dreq->dreq_iss     = dccp_v4_init_sequence(sk, skb);
+       dreq->dreq_service = service;
 
        if (dccp_v4_send_response(sk, req, dst))
                goto drop_and_free;
@@ -1284,6 +1302,7 @@ static int dccp_v4_init_sock(struct sock *sk)
        sk->sk_write_space = dccp_write_space;
        dp->dccps_mss_cache = 536;
        dp->dccps_role = DCCP_ROLE_UNDEFINED;
+       dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
 
        return 0;
 }
@@ -1305,6 +1324,11 @@ static int dccp_v4_destroy_sock(struct sock *sk)
        if (inet_csk(sk)->icsk_bind_hash != NULL)
                inet_put_port(&dccp_hashinfo, sk);
 
+       if (dp->dccps_service_list != NULL) {
+               kfree(dp->dccps_service_list);
+               dp->dccps_service_list = NULL;
+       }
+
        ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk);
        ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk);
        dccp_ackpkts_free(dp->dccps_hc_rx_ackpkts);
index 18461bc04cbefae76dc8073878a53ec51bda0885..933e10db1789e9a0db1682eff3073eb269144c8b 100644 (file)
@@ -93,9 +93,11 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
                struct inet_connection_sock *newicsk = inet_csk(sk);
                struct dccp_sock *newdp = dccp_sk(newsk);
 
+               newdp->dccps_role          = DCCP_ROLE_SERVER;
                newdp->dccps_hc_rx_ackpkts = NULL;
-               newdp->dccps_role = DCCP_ROLE_SERVER;
-               newicsk->icsk_rto = DCCP_TIMEOUT_INIT;
+               newdp->dccps_service_list  = NULL;
+               newdp->dccps_service       = dreq->dreq_service;
+               newicsk->icsk_rto          = DCCP_TIMEOUT_INIT;
                do_gettimeofday(&newdp->dccps_epoch);
 
                if (newdp->dccps_options.dccpo_send_ack_vector) {
index ea6d0e91e5117ac8f5ca1b5dbbce4a5c53269696..156b1d29a156ccd6d83de86d343c527cfbb5cf3f 100644 (file)
@@ -85,7 +85,7 @@ int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
                switch (dcb->dccpd_type) {
                case DCCP_PKT_REQUEST:
                        dccp_hdr_request(skb)->dccph_req_service =
-                                                       dcb->dccpd_service;
+                                                       dp->dccps_service;
                        break;
                case DCCP_PKT_RESET:
                        dccp_hdr_reset(skb)->dccph_reset_code =
@@ -270,6 +270,7 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
                                   struct request_sock *req)
 {
        struct dccp_hdr *dh;
+       struct dccp_request_sock *dreq;
        const int dccp_header_size = sizeof(struct dccp_hdr) +
                                     sizeof(struct dccp_hdr_ext) +
                                     sizeof(struct dccp_hdr_response);
@@ -285,8 +286,9 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
        skb->dst = dst_clone(dst);
        skb->csum = 0;
 
+       dreq = dccp_rsk(req);
        DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE;
-       DCCP_SKB_CB(skb)->dccpd_seq  = dccp_rsk(req)->dreq_iss;
+       DCCP_SKB_CB(skb)->dccpd_seq  = dreq->dreq_iss;
        dccp_insert_options(sk, skb);
 
        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,
                           DCCP_SKB_CB(skb)->dccpd_opt_len) / 4;
        dh->dccph_type  = DCCP_PKT_RESPONSE;
        dh->dccph_x     = 1;
-       dccp_hdr_set_seq(dh, dccp_rsk(req)->dreq_iss);
-       dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dccp_rsk(req)->dreq_isr);
+       dccp_hdr_set_seq(dh, dreq->dreq_iss);
+       dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr);
+       dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service;
 
        dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr,
                                              inet_rsk(req)->rmt_addr);
@@ -397,9 +400,6 @@ int dccp_connect(struct sock *sk)
        skb_reserve(skb, MAX_DCCP_HEADER);
 
        DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST;
-       /* FIXME: set service to something meaningful, coming
-        * from userspace*/
-       DCCP_SKB_CB(skb)->dccpd_service = 0;
        skb->csum = 0;
        skb_set_owner_w(skb, sk);
 
index 18a0e69c9dc75f709e15be01e7ef6def17de3eb5..9bda2868eba669cc387ca4035e000b53b6dfc3ad 100644 (file)
@@ -94,7 +94,15 @@ EXPORT_SYMBOL_GPL(dccp_state_name);
 
 static inline int dccp_listen_start(struct sock *sk)
 {
-       dccp_sk(sk)->dccps_role = DCCP_ROLE_LISTEN;
+       struct dccp_sock *dp = dccp_sk(sk);
+
+       dp->dccps_role = DCCP_ROLE_LISTEN;
+       /*
+        * Apps need to use setsockopt(DCCP_SOCKOPT_SERVICE)
+        * before calling listen()
+        */
+       if (dccp_service_not_initialized(sk))
+               return -EPROTO;
        return inet_csk_listen_start(sk, TCP_SYNQ_HSIZE);
 }
 
@@ -202,6 +210,42 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
        return -ENOIOCTLCMD;
 }
 
+static int dccp_setsockopt_service(struct sock *sk, const u32 service,
+                                  char __user *optval, int optlen)
+{
+       struct dccp_sock *dp = dccp_sk(sk);
+       struct dccp_service_list *sl = NULL;
+
+       if (service == DCCP_SERVICE_INVALID_VALUE || 
+           optlen > DCCP_SERVICE_LIST_MAX_LEN * sizeof(u32))
+               return -EINVAL;
+
+       if (optlen > sizeof(service)) {
+               sl = kmalloc(optlen, GFP_KERNEL);
+               if (sl == NULL)
+                       return -ENOMEM;
+
+               sl->dccpsl_nr = optlen / sizeof(u32) - 1;
+               if (copy_from_user(sl->dccpsl_list,
+                                  optval + sizeof(service),
+                                  optlen - sizeof(service)) ||
+                   dccp_list_has_service(sl, DCCP_SERVICE_INVALID_VALUE)) {
+                       kfree(sl);
+                       return -EFAULT;
+               }
+       }
+
+       lock_sock(sk);
+       dp->dccps_service = service;
+
+       if (dp->dccps_service_list != NULL)
+               kfree(dp->dccps_service_list);
+
+       dp->dccps_service_list = sl;
+       release_sock(sk);
+       return 0;
+}
+
 int dccp_setsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int optlen)
 {
@@ -218,8 +262,10 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
        if (get_user(val, (int __user *)optval))
                return -EFAULT;
 
-       lock_sock(sk);
+       if (optname == DCCP_SOCKOPT_SERVICE)
+               return dccp_setsockopt_service(sk, val, optval, optlen);
 
+       lock_sock(sk);
        dp = dccp_sk(sk);
        err = 0;
 
@@ -236,6 +282,37 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
+static int dccp_getsockopt_service(struct sock *sk, int len,
+                                  u32 __user *optval,
+                                  int __user *optlen)
+{
+       const struct dccp_sock *dp = dccp_sk(sk);
+       const struct dccp_service_list *sl;
+       int err = -ENOENT, slen = 0, total_len = sizeof(u32);
+
+       lock_sock(sk);
+       if (dccp_service_not_initialized(sk))
+               goto out;
+
+       if ((sl = dp->dccps_service_list) != NULL) {
+               slen = sl->dccpsl_nr * sizeof(u32);
+               total_len += slen;
+       }
+
+       err = -EINVAL;
+       if (total_len > len)
+               goto out;
+
+       err = 0;
+       if (put_user(total_len, optlen) ||
+           put_user(dp->dccps_service, optval) ||
+           (sl != NULL && copy_to_user(optval + 1, sl->dccpsl_list, slen)))
+               err = -EFAULT;
+out:
+       release_sock(sk);
+       return err;
+}
+
 int dccp_getsockopt(struct sock *sk, int level, int optname,
                    char __user *optval, int __user *optlen)
 {
@@ -248,6 +325,10 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
        if (get_user(len, optlen))
                return -EFAULT;
 
+       if (optname == DCCP_SOCKOPT_SERVICE)
+               return dccp_getsockopt_service(sk, len,
+                                              (u32 __user *)optval, optlen);
+
        len = min_t(unsigned int, len, sizeof(int));
        if (len < 0)
                return -EINVAL;