summaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c64
-rw-r--r--net/sctp/input.c4
-rw-r--r--net/sctp/output.c23
-rw-r--r--net/sctp/protocol.c10
-rw-r--r--net/sctp/sm_make_chunk.c16
-rw-r--r--net/sctp/sm_sideeffect.c8
-rw-r--r--net/sctp/sm_statefuns.c14
-rw-r--r--net/sctp/sm_statetable.c2
-rw-r--r--net/sctp/socket.c48
-rw-r--r--net/sctp/sysctl.c6
-rw-r--r--net/sctp/ulpevent.c7
11 files changed, 127 insertions, 75 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index f4b23043b610..525864bf4f07 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -293,7 +293,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
* told otherwise.
*/
asoc->peer.ipv4_address = 1;
- asoc->peer.ipv6_address = 1;
+ if (asoc->base.sk->sk_family == PF_INET6)
+ asoc->peer.ipv6_address = 1;
INIT_LIST_HEAD(&asoc->asocs);
asoc->autoclose = sp->autoclose;
@@ -566,6 +567,21 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
if (asoc->init_last_sent_to == peer)
asoc->init_last_sent_to = NULL;
+ /* If we remove the transport an SHUTDOWN was last sent to, set it
+ * to NULL. Combined with the update of the retran path above, this
+ * will cause the next SHUTDOWN to be sent to the next available
+ * transport, maintaining the cycle.
+ */
+ if (asoc->shutdown_last_sent_to == peer)
+ asoc->shutdown_last_sent_to = NULL;
+
+ /* If we remove the transport an ASCONF was last sent to, set it to
+ * NULL.
+ */
+ if (asoc->addip_last_asconf &&
+ asoc->addip_last_asconf->transport == peer)
+ asoc->addip_last_asconf->transport = NULL;
+
asoc->peer.transport_count--;
sctp_transport_free(peer);
@@ -1268,49 +1284,21 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc)
ntohs(t->ipaddr.v4.sin_port));
}
-/* Choose the transport for sending a INIT packet. */
-struct sctp_transport *sctp_assoc_choose_init_transport(
- struct sctp_association *asoc)
-{
- struct sctp_transport *t;
-
- /* Use the retran path. If the last INIT was sent over the
- * retran path, update the retran path and use it.
- */
- if (!asoc->init_last_sent_to) {
- t = asoc->peer.active_path;
- } else {
- if (asoc->init_last_sent_to == asoc->peer.retran_path)
- sctp_assoc_update_retran_path(asoc);
- t = asoc->peer.retran_path;
- }
-
- SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
- " %p addr: ",
- " port: %d\n",
- asoc,
- (&t->ipaddr),
- ntohs(t->ipaddr.v4.sin_port));
-
- return t;
-}
-
-/* Choose the transport for sending a SHUTDOWN packet. */
-struct sctp_transport *sctp_assoc_choose_shutdown_transport(
- struct sctp_association *asoc)
+/* Choose the transport for sending retransmit packet. */
+struct sctp_transport *sctp_assoc_choose_alter_transport(
+ struct sctp_association *asoc, struct sctp_transport *last_sent_to)
{
- /* If this is the first time SHUTDOWN is sent, use the active path,
- * else use the retran path. If the last SHUTDOWN was sent over the
+ /* If this is the first time packet is sent, use the active path,
+ * else use the retran path. If the last packet was sent over the
* retran path, update the retran path and use it.
*/
- if (!asoc->shutdown_last_sent_to)
+ if (!last_sent_to)
return asoc->peer.active_path;
else {
- if (asoc->shutdown_last_sent_to == asoc->peer.retran_path)
+ if (last_sent_to == asoc->peer.retran_path)
sctp_assoc_update_retran_path(asoc);
return asoc->peer.retran_path;
}
-
}
/* Update the association's pmtu and frag_point by going through all the
@@ -1482,6 +1470,10 @@ int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp)
{
int assoc_id;
int error = 0;
+
+ /* If the id is already assigned, keep it. */
+ if (asoc->assoc_id)
+ return error;
retry:
if (unlikely(!idr_pre_get(&sctp_assocs_id, gfp)))
return -ENOMEM;
diff --git a/net/sctp/input.c b/net/sctp/input.c
index d2e98803ffe3..c0c973e67add 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -81,13 +81,13 @@ static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb);
/* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb)
{
- struct sk_buff *list = skb_shinfo(skb)->frag_list;
struct sctphdr *sh = sctp_hdr(skb);
__le32 cmp = sh->checksum;
+ struct sk_buff *list;
__le32 val;
__u32 tmp = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
- for (; list; list = list->next)
+ skb_walk_frags(skb, list)
tmp = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
tmp);
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 7d08f522ec84..b76411444515 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -405,13 +405,14 @@ int sctp_packet_transmit(struct sctp_packet *packet)
sctp_assoc_sync_pmtu(asoc);
}
}
- nskb->dst = dst_clone(tp->dst);
- if (!nskb->dst)
+ dst = dst_clone(tp->dst);
+ skb_dst_set(nskb, dst);
+ if (dst)
goto no_route;
- dst = nskb->dst;
/* Build the SCTP header. */
sh = (struct sctphdr *)skb_push(nskb, sizeof(struct sctphdr));
+ skb_reset_transport_header(nskb);
sh->source = htons(packet->source_port);
sh->dest = htons(packet->destination_port);
@@ -527,15 +528,25 @@ int sctp_packet_transmit(struct sctp_packet *packet)
* Note: Adler-32 is no longer applicable, as has been replaced
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
*/
- if (!sctp_checksum_disable && !(dst->dev->features & NETIF_F_NO_CSUM)) {
+ if (!sctp_checksum_disable &&
+ !(dst->dev->features & (NETIF_F_NO_CSUM | NETIF_F_SCTP_CSUM))) {
__u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len);
/* 3) Put the resultant value into the checksum field in the
* common header, and leave the rest of the bits unchanged.
*/
sh->checksum = sctp_end_cksum(crc32);
- } else
- nskb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else {
+ if (dst->dev->features & NETIF_F_SCTP_CSUM) {
+ /* no need to seed psuedo checksum for SCTP */
+ nskb->ip_summed = CHECKSUM_PARTIAL;
+ nskb->csum_start = (skb_transport_header(nskb) -
+ nskb->head);
+ nskb->csum_offset = offsetof(struct sctphdr, checksum);
+ } else {
+ nskb->ip_summed = CHECKSUM_UNNECESSARY;
+ }
+ }
/* IP layer ECN support
* From RFC 2481
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 8eb3e61cb701..79cbd47f4df7 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -393,7 +393,7 @@ static int sctp_v4_addr_valid(union sctp_addr *addr,
return 0;
/* Is this a broadcast address? */
- if (skb && skb->rtable->rt_flags & RTCF_BROADCAST)
+ if (skb && skb_rtable(skb)->rt_flags & RTCF_BROADCAST)
return 0;
return 1;
@@ -572,7 +572,7 @@ static void sctp_v4_get_saddr(struct sctp_sock *sk,
/* What interface did this skb arrive on? */
static int sctp_v4_skb_iif(const struct sk_buff *skb)
{
- return skb->rtable->rt_iif;
+ return skb_rtable(skb)->rt_iif;
}
/* Was this packet marked by Explicit Congestion Notification? */
@@ -848,8 +848,8 @@ static inline int sctp_v4_xmit(struct sk_buff *skb,
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n",
__func__, skb, skb->len,
- &skb->rtable->rt_src,
- &skb->rtable->rt_dst);
+ &skb_rtable(skb)->rt_src,
+ &skb_rtable(skb)->rt_dst);
inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ?
IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
@@ -1370,6 +1370,8 @@ SCTP_STATIC __exit void sctp_exit(void)
sctp_proc_exit();
cleanup_sctp_mibs();
+ rcu_barrier(); /* Wait for completion of call_rcu()'s */
+
kmem_cache_destroy(sctp_chunk_cachep);
kmem_cache_destroy(sctp_bucket_cachep);
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 6851ee94e974..61cc6075b0df 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2864,19 +2864,19 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
switch (addr_param->v4.param_hdr.type) {
case SCTP_PARAM_IPV6_ADDRESS:
if (!asoc->peer.ipv6_address)
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (!asoc->peer.ipv4_address)
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
break;
default:
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
}
af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type));
if (unlikely(!af))
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
af->from_addr_param(&addr, addr_param, htons(asoc->peer.port), 0);
@@ -2886,7 +2886,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
* make sure we check for that)
*/
if (!af->is_any(&addr) && !af->addr_valid(&addr, NULL, asconf->skb))
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
switch (asconf_param->param_hdr.type) {
case SCTP_PARAM_ADD_IP:
@@ -2954,12 +2954,12 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
peer = sctp_assoc_lookup_paddr(asoc, &addr);
if (!peer)
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_DNS_FAILED;
sctp_assoc_set_primary(asoc, peer);
break;
default:
- return SCTP_ERROR_INV_PARAM;
+ return SCTP_ERROR_UNKNOWN_PARAM;
break;
}
@@ -3273,7 +3273,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
retval = 1;
break;
- case SCTP_ERROR_INV_PARAM:
+ case SCTP_ERROR_UNKNOWN_PARAM:
/* Disable sending this type of asconf parameter in
* future.
*/
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index e2020eb2c8ca..86426aac1600 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -686,7 +686,8 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds,
{
struct sctp_transport *t;
- t = sctp_assoc_choose_shutdown_transport(asoc);
+ t = sctp_assoc_choose_alter_transport(asoc,
+ asoc->shutdown_last_sent_to);
asoc->shutdown_last_sent_to = t;
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t;
@@ -777,7 +778,7 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds,
{
struct sctp_transport *t;
- t = asoc->peer.active_path;
+ t = sctp_assoc_choose_alter_transport(asoc, chunk->transport);
asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto;
chunk->transport = t;
}
@@ -1379,7 +1380,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
chunk = cmd->obj.ptr;
- t = sctp_assoc_choose_init_transport(asoc);
+ t = sctp_assoc_choose_alter_transport(asoc,
+ asoc->init_last_sent_to);
asoc->init_last_sent_to = t;
chunk->transport = t;
t->init_sent_count++;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 55a61aa69662..7288192f7df5 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -5432,9 +5432,13 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const struct sctp_endpoint *ep,
if (!reply)
goto nomem;
- /* Do some failure management (Section 8.2). */
- sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
- SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
+ /* Do some failure management (Section 8.2).
+ * If we remove the transport an SHUTDOWN was last sent to, don't
+ * do failure management.
+ */
+ if (asoc->shutdown_last_sent_to)
+ sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+ SCTP_TRANSPORT(asoc->shutdown_last_sent_to));
/* Set the transport for the SHUTDOWN/ACK chunk and the timeout for
* the T2-shutdown timer.
@@ -5471,7 +5475,9 @@ sctp_disposition_t sctp_sf_t4_timer_expire(
* detection on the appropriate destination address as defined in
* RFC2960 [5] section 8.1 and 8.2.
*/
- sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport));
+ if (transport)
+ sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE,
+ SCTP_TRANSPORT(transport));
/* Reconfig T4 timer and transport. */
sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T4, SCTP_CHUNK(chunk));
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index 5c8186d88c61..6d9b3aafcc5d 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -698,7 +698,7 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_FUNC(sctp_sf_do_prm_asconf), \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
-} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
+} /* TYPE_SCTP_PRIMITIVE_ASCONF */
/* The primary index for this table is the primitive type.
* The secondary index for this table is the state.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 5fb3a8c9792e..0f01e5d8a24f 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1100,6 +1100,15 @@ static int __sctp_connect(struct sock* sk,
goto out_free;
}
+ /* In case the user of sctp_connectx() wants an association
+ * id back, assign one now.
+ */
+ if (assoc_id) {
+ err = sctp_assoc_set_id(asoc, GFP_KERNEL);
+ if (err < 0)
+ goto out_free;
+ }
+
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) {
goto out_free;
@@ -1120,7 +1129,7 @@ static int __sctp_connect(struct sock* sk,
timeo = sock_sndtimeo(sk, f_flags & O_NONBLOCK);
err = sctp_wait_for_connect(asoc, &timeo);
- if (!err && assoc_id)
+ if ((err == 0 || err == -EINPROGRESS) && assoc_id)
*assoc_id = asoc->assoc_id;
/* Don't free association on exit. */
@@ -1264,6 +1273,34 @@ SCTP_STATIC int sctp_setsockopt_connectx(struct sock* sk,
return assoc_id;
}
+/*
+ * New (hopefully final) interface for the API. The option buffer is used
+ * both for the returned association id and the addresses.
+ */
+SCTP_STATIC int sctp_getsockopt_connectx3(struct sock* sk, int len,
+ char __user *optval,
+ int __user *optlen)
+{
+ sctp_assoc_t assoc_id = 0;
+ int err = 0;
+
+ if (len < sizeof(assoc_id))
+ return -EINVAL;
+
+ err = __sctp_setsockopt_connectx(sk,
+ (struct sockaddr __user *)(optval + sizeof(assoc_id)),
+ len - sizeof(assoc_id), &assoc_id);
+
+ if (err == 0 || err == -EINPROGRESS) {
+ if (copy_to_user(optval, &assoc_id, sizeof(assoc_id)))
+ return -EFAULT;
+ if (put_user(sizeof(assoc_id), optlen))
+ return -EFAULT;
+ }
+
+ return err;
+}
+
/* API 3.1.4 close() - UDP Style Syntax
* Applications use close() to perform graceful shutdown (as described in
* Section 10.1 of [SCTP]) on ALL the associations currently represented
@@ -1844,7 +1881,7 @@ static int sctp_skb_pull(struct sk_buff *skb, int len)
len -= skb_len;
__skb_pull(skb, skb_len);
- for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
+ skb_walk_frags(skb, list) {
rlen = sctp_skb_pull(list, len);
skb->len -= (len-rlen);
skb->data_len -= (len-rlen);
@@ -5578,6 +5615,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_local_addrs(sk, len, optval,
optlen);
break;
+ case SCTP_SOCKOPT_CONNECTX3:
+ retval = sctp_getsockopt_connectx3(sk, len, optval, optlen);
+ break;
case SCTP_DEFAULT_SEND_PARAM:
retval = sctp_getsockopt_default_send_param(sk, len,
optval, optlen);
@@ -6620,7 +6660,7 @@ static void sctp_sock_rfree_frag(struct sk_buff *skb)
goto done;
/* Don't forget the fragments. */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+ skb_walk_frags(skb, frag)
sctp_sock_rfree_frag(frag);
done:
@@ -6635,7 +6675,7 @@ static void sctp_skb_set_owner_r_frag(struct sk_buff *skb, struct sock *sk)
goto done;
/* Don't forget the fragments. */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next)
+ skb_walk_frags(skb, frag)
sctp_skb_set_owner_r_frag(frag, sk);
done:
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index f58e994e6852..63eabbc71298 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -49,8 +49,8 @@ static int zero = 0;
static int one = 1;
static int timer_max = 86400000; /* ms in one day */
static int int_max = INT_MAX;
-static long sack_timer_min = 1;
-static long sack_timer_max = 500;
+static int sack_timer_min = 1;
+static int sack_timer_max = 500;
extern int sysctl_sctp_mem[3];
extern int sysctl_sctp_rmem[3];
@@ -223,7 +223,7 @@ static ctl_table sctp_table[] = {
.ctl_name = NET_SCTP_SACK_TIMEOUT,
.procname = "sack_timeout",
.data = &sctp_sack_timeout,
- .maxlen = sizeof(long),
+ .maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.strategy = sysctl_intvec,
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 5f186ca550d7..8b3560fd876d 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -976,9 +976,8 @@ static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
* In general, the skb passed from IP can have only 1 level of
* fragments. But we allow multiple levels of fragments.
*/
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ skb_walk_frags(skb, frag)
sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
- }
}
/* Do accounting for bytes just read by user and release the references to
@@ -1003,7 +1002,7 @@ static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
goto done;
/* Don't forget the fragments. */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ skb_walk_frags(skb, frag) {
/* NOTE: skb_shinfos are recursive. Although IP returns
* skb's with only 1 level of fragments, SCTP reassembly can
* increase the levels.
@@ -1026,7 +1025,7 @@ static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
goto done;
/* Don't forget the fragments. */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
+ skb_walk_frags(skb, frag) {
/* NOTE: skb_shinfos are recursive. Although IP returns
* skb's with only 1 level of fragments, SCTP reassembly can
* increase the levels.