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)
}
/* 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);
pmtu -= sizeof(struct sctp_sack_chunk);
int frag = pmtu;
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
......@@ -574,21 +578,24 @@ struct sctp6_sock {
#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))
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;
}
/* Is the association in this 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)
{
return asoc->state == state;
}
/* Is the socket in this 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;
}
......
......@@ -292,10 +292,12 @@ struct sctp_opt {
struct sctp_rtoinfo rtoinfo;
struct sctp_paddrparams paddrparam;
struct sctp_event_subscribe subscribe;
int user_frag;
__u32 autoclose;
__u8 nodelay;
__u8 disable_fragments;
__u8 pd_mode;
__u8 v4mapped;
/* Receive to here while partial delivery is in effect. */
struct sk_buff_head pd_lobby;
......
......@@ -110,6 +110,10 @@ enum sctp_optname {
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
SCTP_NODELAY, /* Get/set nodelay option. */
#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,
struct sctp_opt *sp;
unsigned short port;
sp = sctp_sk(asoc->base.sk);
/* AF_INET and AF_INET6 share common port field. */
port = addr->v4.sin_port;
/* Set the port if it has not been set yet. */
if (0 == asoc->peer.port) {
if (0 == asoc->peer.port)
asoc->peer.port = port;
}
/* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr);
......@@ -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 "
"%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
* initialize the packet structure anyway.
......@@ -471,7 +472,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Initialize the peer's heartbeat interval based on the
* sock configured value.
*/
sp = sctp_sk(asoc->base.sk);
peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ;
/* Attach the remote transport to our asoc. */
......@@ -985,8 +986,9 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
}
if (pmtu) {
struct sctp_opt *sp = sctp_sk(asoc->base.sk);
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",
......
......@@ -158,6 +158,11 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
max -= sizeof(struct sctp_data_chunk);
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;
/* Encourage Cookie-ECHO bundling. */
......@@ -177,7 +182,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
over = msg_len % max;
offset = 0;
if (whole && over)
if ((whole > 1) || (whole && over))
SCTP_INC_STATS_USER(SctpFragUsrMsgs);
/* Create chunks for all the full sized DATA chunks. */
......
......@@ -1452,7 +1452,6 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
}
/*
*
* 7.1.5 SCTP_NODELAY
*
* 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)
static int sctp_setsockopt_nodelay(struct sock *sk, char *optval,
int optlen)
{
__u8 val;
int val;
if (optlen < sizeof(__u8))
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (__u8 *)optval))
if (get_user(val, (int *)optval))
return -EFAULT;
sctp_sk(sk)->nodelay = (val == 0) ? 0 : 1;
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()
*
* 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,
case SCTP_INITMSG:
retval = sctp_setsockopt_initmsg(sk, optval, optlen);
break;
case SCTP_SET_DEFAULT_SEND_PARAM:
retval = sctp_setsockopt_default_send_param(sk, optval,
optlen);
break;
case SCTP_SET_PEER_PRIMARY_ADDR:
retval = sctp_setsockopt_peer_prim(sk, optval, optlen);
break;
case SCTP_NODELAY:
retval = sctp_setsockopt_nodelay(sk, optval, optlen);
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:
retval = -ENOPROTOOPT;
break;
......@@ -1861,12 +1910,19 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Turn on/off any Nagle-like algorithm. */
sp->nodelay = 1;
/* Enable by default. */
sp->v4mapped = 1;
/* Auto-close idle associations after the configured
* number of seconds. A value of 0 disables this
* feature. Configure through the SCTP_AUTOCLOSE socket option,
* for UDP-style sockets only.
*/
sp->autoclose = 0;
/* User specified fragmentation limit. */
sp->user_frag = 0;
sp->pf = sctp_get_pf_specific(sk->family);
/* Control variables for partial data delivery. */
......@@ -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_instrms = asoc->c.sinit_max_instreams;
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_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
......@@ -2419,12 +2479,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
static int sctp_getsockopt_nodelay(struct sock *sk, int len,
char *optval, int *optlen)
{
__u8 val;
int val;
if (len < sizeof(__u8))
if (len < sizeof(int))
return -EINVAL;
len = sizeof(__u8);
len = sizeof(int);
val = (sctp_sk(sk)->nodelay == 1);
if (put_user(len, optlen))
return -EFAULT;
......@@ -2432,6 +2492,62 @@ static int sctp_getsockopt_nodelay(struct sock *sk, int len,
return -EFAULT;
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,
char *optval, int *optlen)
......@@ -2509,6 +2625,12 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_NODELAY:
retval = sctp_getsockopt_nodelay(sk, len, optval, optlen);
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:
retval = -ENOPROTOOPT;
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