Commit 99c8d97d authored by Jon Grimm's avatar Jon Grimm

[SCTP] Add SCTP_MAXSEG sockopt.

We'll fragment (if fragmentation not disabled) to the min of maxseg
or PMTU allowance.  
parent 7160b90e
...@@ -430,12 +430,16 @@ static inline __s32 sctp_jitter(__u32 rto) ...@@ -430,12 +430,16 @@ static inline __s32 sctp_jitter(__u32 rto)
} }
/* Break down data chunks at this point. */ /* Break down data chunks at this point. */
static inline int sctp_frag_point(int pmtu) static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu)
{ {
pmtu -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk); int frag = pmtu;
pmtu -= sizeof(struct sctp_sack_chunk); frag -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk);
frag -= sizeof(struct sctp_sack_chunk);
return pmtu; if (sp->user_frag)
frag = min_t(int, frag, sp->user_frag);
return frag;
} }
/* Walk through a list of TLV parameters. Don't trust the /* Walk through a list of TLV parameters. Don't trust the
...@@ -574,21 +578,24 @@ struct sctp6_sock { ...@@ -574,21 +578,24 @@ struct sctp6_sock {
#define sctp_sk(__sk) (&((struct sctp_sock *)__sk)->sctp) #define sctp_sk(__sk) (&((struct sctp_sock *)__sk)->sctp)
/* Is a socket of this style? */
#define sctp_style(sk, style) __sctp_style((sk), (SCTP_SOCKET_##style)) #define sctp_style(sk, style) __sctp_style((sk), (SCTP_SOCKET_##style))
int static inline __sctp_style(struct sock *sk, sctp_socket_type_t style) int static inline __sctp_style(const struct sock *sk, sctp_socket_type_t style)
{ {
return sctp_sk(sk)->type == style; return sctp_sk(sk)->type == style;
} }
/* Is the association in this state? */
#define sctp_state(asoc, state) __sctp_state((asoc), (SCTP_STATE_##state)) #define sctp_state(asoc, state) __sctp_state((asoc), (SCTP_STATE_##state))
int static inline __sctp_state(struct sctp_association *asoc, int static inline __sctp_state(const struct sctp_association *asoc,
sctp_state_t state) sctp_state_t state)
{ {
return asoc->state == state; return asoc->state == state;
} }
/* Is the socket in this state? */
#define sctp_sstate(sk, state) __sctp_sstate((sk), (SCTP_SS_##state)) #define sctp_sstate(sk, state) __sctp_sstate((sk), (SCTP_SS_##state))
int static inline __sctp_sstate(struct sock *sk, sctp_sock_state_t state) int static inline __sctp_sstate(const struct sock *sk, sctp_sock_state_t state)
{ {
return sk->state == state; return sk->state == state;
} }
......
...@@ -292,10 +292,12 @@ struct sctp_opt { ...@@ -292,10 +292,12 @@ struct sctp_opt {
struct sctp_rtoinfo rtoinfo; struct sctp_rtoinfo rtoinfo;
struct sctp_paddrparams paddrparam; struct sctp_paddrparams paddrparam;
struct sctp_event_subscribe subscribe; struct sctp_event_subscribe subscribe;
int user_frag;
__u32 autoclose; __u32 autoclose;
__u8 nodelay; __u8 nodelay;
__u8 disable_fragments; __u8 disable_fragments;
__u8 pd_mode; __u8 pd_mode;
__u8 v4mapped;
/* Receive to here while partial delivery is in effect. */ /* Receive to here while partial delivery is in effect. */
struct sk_buff_head pd_lobby; struct sk_buff_head pd_lobby;
......
...@@ -110,6 +110,10 @@ enum sctp_optname { ...@@ -110,6 +110,10 @@ enum sctp_optname {
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS #define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
SCTP_NODELAY, /* Get/set nodelay option. */ SCTP_NODELAY, /* Get/set nodelay option. */
#define SCTP_NODELAY SCTP_NODELAY #define SCTP_NODELAY SCTP_NODELAY
SCTP_I_WANT_MAPPED_V4_ADDR, /* Turn on/off mapped v4 addresses */
#define SCTP_I_WANT_MAPPED_V4_ADDR SCTP_I_WANT_MAPPED_V4_ADDR
SCTP_MAXSEG, /* Get/set maximum fragment. */
#define SCTP_MAXSEG SCTP_MAXSEG
}; };
......
...@@ -391,13 +391,14 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -391,13 +391,14 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
struct sctp_opt *sp; struct sctp_opt *sp;
unsigned short port; unsigned short port;
sp = sctp_sk(asoc->base.sk);
/* AF_INET and AF_INET6 share common port field. */ /* AF_INET and AF_INET6 share common port field. */
port = addr->v4.sin_port; port = addr->v4.sin_port;
/* Set the port if it has not been set yet. */ /* Set the port if it has not been set yet. */
if (0 == asoc->peer.port) { if (0 == asoc->peer.port)
asoc->peer.port = port; asoc->peer.port = port;
}
/* Check to see if this is a duplicate. */ /* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr); peer = sctp_assoc_lookup_paddr(asoc, addr);
...@@ -426,7 +427,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -426,7 +427,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
"%d\n", asoc, asoc->pmtu); "%d\n", asoc, asoc->pmtu);
asoc->frag_point = sctp_frag_point(asoc->pmtu); asoc->frag_point = sctp_frag_point(sp, asoc->pmtu);
/* The asoc->peer.port might not be meaningful yet, but /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway. * initialize the packet structure anyway.
...@@ -471,7 +472,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, ...@@ -471,7 +472,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Initialize the peer's heartbeat interval based on the /* Initialize the peer's heartbeat interval based on the
* sock configured value. * sock configured value.
*/ */
sp = sctp_sk(asoc->base.sk);
peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ; peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ;
/* Attach the remote transport to our asoc. */ /* Attach the remote transport to our asoc. */
...@@ -985,8 +986,9 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc) ...@@ -985,8 +986,9 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
} }
if (pmtu) { if (pmtu) {
struct sctp_opt *sp = sctp_sk(asoc->base.sk);
asoc->pmtu = pmtu; asoc->pmtu = pmtu;
asoc->frag_point = sctp_frag_point(pmtu); asoc->frag_point = sctp_frag_point(sp, pmtu);
} }
SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
......
...@@ -158,6 +158,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -158,6 +158,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
max -= sizeof(struct sctp_data_chunk); max -= sizeof(struct sctp_data_chunk);
whole = 0; whole = 0;
/* If user has specified smaller fragmentation, make it so. */
if (sctp_sk(asoc->base.sk)->user_frag)
max = min_t(int, max, sctp_sk(asoc->base.sk)->user_frag);
first_len = max; first_len = max;
/* Encourage Cookie-ECHO bundling. */ /* Encourage Cookie-ECHO bundling. */
...@@ -177,7 +182,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, ...@@ -177,7 +182,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
over = msg_len % max; over = msg_len % max;
offset = 0; offset = 0;
if (whole && over) if ((whole > 1) || (whole && over))
SCTP_INC_STATS_USER(SctpFragUsrMsgs); SCTP_INC_STATS_USER(SctpFragUsrMsgs);
/* Create chunks for all the full sized DATA chunks. */ /* Create chunks for all the full sized DATA chunks. */
......
...@@ -894,10 +894,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -894,10 +894,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
SCTP_DEBUG_PRINTK("Just looked up association: " SCTP_DEBUG_PRINTK("Just looked up association: "
"%s. \n", asoc->debug_name); "%s. \n", asoc->debug_name);
/* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED /* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED
* socket that has an association in CLOSED state. This can * socket that has an association in CLOSED state. This can
* happen when an accepted socket has an association that is * happen when an accepted socket has an association that is
* already CLOSED. * already CLOSED.
*/ */
if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP)) { if (sctp_state(asoc, CLOSED) && sctp_style(sk, TCP)) {
err = -EPIPE; err = -EPIPE;
...@@ -1082,10 +1082,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1082,10 +1082,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
list_for_each_safe(pos, temp, &datamsg->chunks) { list_for_each_safe(pos, temp, &datamsg->chunks) {
chunk = list_entry(pos, struct sctp_chunk, frag_list); chunk = list_entry(pos, struct sctp_chunk, frag_list);
list_del_init(pos); list_del_init(pos);
/* Do accounting for the write space. */ /* Do accounting for the write space. */
sctp_set_owner_w(chunk); sctp_set_owner_w(chunk);
chunk->transport = chunk_tp; chunk->transport = chunk_tp;
/* Send it to the lower layers. */ /* Send it to the lower layers. */
...@@ -1452,7 +1452,6 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen) ...@@ -1452,7 +1452,6 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
} }
/* /*
*
* 7.1.5 SCTP_NODELAY * 7.1.5 SCTP_NODELAY
* *
* Turn on/off any Nagle-like algorithm. This means that packets are * Turn on/off any Nagle-like algorithm. This means that packets are
...@@ -1463,17 +1462,65 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen) ...@@ -1463,17 +1462,65 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
static int sctp_setsockopt_nodelay(struct sock *sk, char *optval, static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
int optlen) int optlen)
{ {
__u8 val; int val;
if (optlen < sizeof(__u8)) if (optlen < sizeof(int))
return -EINVAL; return -EINVAL;
if (get_user(val, (__u8 *)optval)) if (get_user(val, (int *)optval))
return -EFAULT; return -EFAULT;
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1; sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
return 0; return 0;
} }
/*
* 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
*
* This socket option is a boolean flag which turns on or off mapped V4
* addresses. If this option is turned on and the socket is type
* PF_INET6, then IPv4 addresses will be mapped to V6 representation.
* If this option is turned off, then no mapping will be done of V4
* addresses and a user will receive both PF_INET6 and PF_INET type
* addresses on the socket.
*/
static int sctp_setsockopt_mappedv4(struct sock *sk, char *optval, int optlen)
{
int val;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int *)optval))
return -EFAULT;
/* FIXME: Put real support here. */
return -ENOPROTOOPT;
}
/*
* 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
*
* This socket option specifies the maximum size to put in any outgoing
* SCTP chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
* the user.
*/
static int sctp_setsockopt_maxseg(struct sock *sk, char *optval, int optlen)
{
int val;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int *)optval))
return -EFAULT;
if ((val < 8) || (val > SCTP_MAX_CHUNK_LEN))
return -EINVAL;
sctp_sk(sk)->user_frag = val;
return 0;
}
/* API 6.2 setsockopt(), getsockopt() /* API 6.2 setsockopt(), getsockopt()
* *
* Applications use setsockopt() and getsockopt() to set or retrieve * Applications use setsockopt() and getsockopt() to set or retrieve
...@@ -1563,20 +1610,22 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1563,20 +1610,22 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
case SCTP_INITMSG: case SCTP_INITMSG:
retval = sctp_setsockopt_initmsg(sk, optval, optlen); retval = sctp_setsockopt_initmsg(sk, optval, optlen);
break; break;
case SCTP_SET_DEFAULT_SEND_PARAM: case SCTP_SET_DEFAULT_SEND_PARAM:
retval = sctp_setsockopt_default_send_param(sk, optval, retval = sctp_setsockopt_default_send_param(sk, optval,
optlen); optlen);
break; break;
case SCTP_SET_PEER_PRIMARY_ADDR: case SCTP_SET_PEER_PRIMARY_ADDR:
retval = sctp_setsockopt_peer_prim(sk, optval, optlen); retval = sctp_setsockopt_peer_prim(sk, optval, optlen);
break; break;
case SCTP_NODELAY: case SCTP_NODELAY:
retval = sctp_setsockopt_nodelay(sk, optval, optlen); retval = sctp_setsockopt_nodelay(sk, optval, optlen);
break; break;
case SCTP_I_WANT_MAPPED_V4_ADDR:
retval = sctp_setsockopt_mappedv4(sk, optval, optlen);
break;
case SCTP_MAXSEG:
retval = sctp_setsockopt_maxseg(sk, optval, optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -1703,7 +1752,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1703,7 +1752,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
/* Initialize sk's dport and daddr for getpeername() */ /* Initialize sk's dport and daddr for getpeername() */
inet_sk(sk)->dport = htons(asoc->peer.port); inet_sk(sk)->dport = htons(asoc->peer.port);
af = sctp_get_af_specific(to.sa.sa_family); af = sctp_get_af_specific(to.sa.sa_family);
af->to_sk_daddr(&to, sk); af->to_sk_daddr(&to, sk);
timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK); timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
err = sctp_wait_for_connect(asoc, &timeo); err = sctp_wait_for_connect(asoc, &timeo);
...@@ -1861,12 +1910,19 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk) ...@@ -1861,12 +1910,19 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Turn on/off any Nagle-like algorithm. */ /* Turn on/off any Nagle-like algorithm. */
sp->nodelay = 1; sp->nodelay = 1;
/* Enable by default. */
sp->v4mapped = 1;
/* Auto-close idle associations after the configured /* Auto-close idle associations after the configured
* number of seconds. A value of 0 disables this * number of seconds. A value of 0 disables this
* feature. Configure through the SCTP_AUTOCLOSE socket option, * feature. Configure through the SCTP_AUTOCLOSE socket option,
* for UDP-style sockets only. * for UDP-style sockets only.
*/ */
sp->autoclose = 0; sp->autoclose = 0;
/* User specified fragmentation limit. */
sp->user_frag = 0;
sp->pf = sctp_get_pf_specific(sk->family); sp->pf = sctp_get_pf_specific(sk->family);
/* Control variables for partial data delivery. */ /* Control variables for partial data delivery. */
...@@ -1978,6 +2034,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1978,6 +2034,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_penddata = asoc->peer.tsn_map.pending_data; status.sstat_penddata = asoc->peer.tsn_map.pending_data;
status.sstat_instrms = asoc->c.sinit_max_instreams; status.sstat_instrms = asoc->c.sinit_max_instreams;
status.sstat_outstrms = asoc->c.sinit_num_ostreams; status.sstat_outstrms = asoc->c.sinit_num_ostreams;
/* Just in time frag_point update. */
if (sctp_sk(sk)->user_frag)
asoc->frag_point
= min_t(int, asoc->frag_point, sctp_sk(sk)->user_frag);
status.sstat_fragmentation_point = asoc->frag_point; status.sstat_fragmentation_point = asoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc); status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address, memcpy(&status.sstat_primary.spinfo_address,
...@@ -2417,14 +2477,14 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, ...@@ -2417,14 +2477,14 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
*/ */
static int sctp_getsockopt_nodelay(struct sock *sk, int len, static int sctp_getsockopt_nodelay(struct sock *sk, int len,
char *optval, int *optlen) char *optval, int *optlen)
{ {
__u8 val; int val;
if (len < sizeof(__u8)) if (len < sizeof(int))
return -EINVAL; return -EINVAL;
len = sizeof(__u8); len = sizeof(int);
val = (sctp_sk(sk)->nodelay == 1); val = (sctp_sk(sk)->nodelay == 1);
if (put_user(len, optlen)) if (put_user(len, optlen))
return -EFAULT; return -EFAULT;
...@@ -2432,6 +2492,62 @@ static int sctp_getsockopt_nodelay(struct sock *sk, int len, ...@@ -2432,6 +2492,62 @@ static int sctp_getsockopt_nodelay(struct sock *sk, int len,
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
/*
* 7.1.16 Set/clear IPv4 mapped addresses (SCTP_I_WANT_MAPPED_V4_ADDR)
*
* This socket option is a boolean flag which turns on or off mapped V4
* addresses. If this option is turned on and the socket is type
* PF_INET6, then IPv4 addresses will be mapped to V6 representation.
* If this option is turned off, then no mapping will be done of V4
* addresses and a user will receive both PF_INET6 and PF_INET type
* addresses on the socket.
*/
static int sctp_getsockopt_mappedv4(struct sock *sk, int len,
char *optval, int *optlen)
{
int val;
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
/* FIXME: Until we have support, return disabled. */
val = 0;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
/*
* 7.1.17 Set the maximum fragrmentation size (SCTP_MAXSEG)
*
* This socket option specifies the maximum size to put in any outgoing
* SCTP chunk. If a message is larger than this size it will be
* fragmented by SCTP into the specified size. Note that the underlying
* SCTP implementation may fragment into smaller sized chunks when the
* PMTU of the underlying association is smaller than the value set by
* the user.
*/
static int sctp_getsockopt_maxseg(struct sock *sk, int len,
char *optval, int *optlen)
{
int val;
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
val = sctp_sk(sk)->user_frag;
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen) char *optval, int *optlen)
...@@ -2509,6 +2625,12 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -2509,6 +2625,12 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_NODELAY: case SCTP_NODELAY:
retval = sctp_getsockopt_nodelay(sk, len, optval, optlen); retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
break; break;
case SCTP_I_WANT_MAPPED_V4_ADDR:
retval = sctp_getsockopt_mappedv4(sk, len, optval, optlen);
break;
case SCTP_MAXSEG:
retval = sctp_getsockopt_maxseg(sk, len, optval, optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment