summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/sctp/associola.c4
-rw-r--r--net/sctp/input.c24
-rw-r--r--net/sctp/socket.c31
-rw-r--r--net/sctp/transport.c39
4 files changed, 74 insertions, 24 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index df94e3cdfba3..498edb0cd4e5 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1231,6 +1231,10 @@ void sctp_assoc_sync_pmtu(struct sctp_association *asoc)
/* Get the lowest pmtu of all the transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);
+ if (t->pmtu_pending && t->dst) {
+ sctp_transport_update_pmtu(t, dst_mtu(t->dst));
+ t->pmtu_pending = 0;
+ }
if (!pmtu || (t->pathmtu < pmtu))
pmtu = t->pathmtu;
}
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 885109fb3dda..d57ff7f3c576 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -367,24 +367,18 @@ static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
struct sctp_transport *t, __u32 pmtu)
{
- if (sock_owned_by_user(sk) || !t || (t->pathmtu == pmtu))
+ if (!t || (t->pathmtu == pmtu))
return;
+ if (sock_owned_by_user(sk)) {
+ asoc->pmtu_pending = 1;
+ t->pmtu_pending = 1;
+ return;
+ }
+
if (t->param_flags & SPP_PMTUD_ENABLE) {
- if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
- printk(KERN_WARNING "%s: Reported pmtu %d too low, "
- "using default minimum of %d\n",
- __FUNCTION__, pmtu,
- SCTP_DEFAULT_MINSEGMENT);
- /* Use default minimum segment size and disable
- * pmtu discovery on this transport.
- */
- t->pathmtu = SCTP_DEFAULT_MINSEGMENT;
- t->param_flags = (t->param_flags & ~SPP_PMTUD) |
- SPP_PMTUD_DISABLE;
- } else {
- t->pathmtu = pmtu;
- }
+ /* Update transports view of the MTU */
+ sctp_transport_update_pmtu(t, pmtu);
/* Update association pmtu. */
sctp_assoc_sync_pmtu(asoc);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 4dcdabf56473..6edaaa009d62 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -333,12 +333,19 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
if (!sp->pf->bind_verify(sp, addr))
return -EADDRNOTAVAIL;
- /* We must either be unbound, or bind to the same port. */
- if (bp->port && (snum != bp->port)) {
- SCTP_DEBUG_PRINTK("sctp_do_bind:"
+ /* We must either be unbound, or bind to the same port.
+ * It's OK to allow 0 ports if we are already bound.
+ * We'll just inhert an already bound port in this case
+ */
+ if (bp->port) {
+ if (!snum)
+ snum = bp->port;
+ else if (snum != bp->port) {
+ SCTP_DEBUG_PRINTK("sctp_do_bind:"
" New port %d does not match existing port "
"%d.\n", snum, bp->port);
- return -EINVAL;
+ return -EINVAL;
+ }
}
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
@@ -1655,6 +1662,9 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
goto out_free;
}
+ if (asoc->pmtu_pending)
+ sctp_assoc_pending_pmtu(asoc);
+
/* If fragmentation is disabled and the message length exceeds the
* association fragmentation point, return EMSGSIZE. The I-D
* does not specify what this error is, but this looks like
@@ -3550,6 +3560,7 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
struct sock *sk = asoc->base.sk;
struct socket *sock;
struct inet_sock *inetsk;
+ struct sctp_af *af;
int err = 0;
/* An association cannot be branched off from an already peeled-off
@@ -3571,8 +3582,9 @@ SCTP_STATIC int sctp_do_peeloff(struct sctp_association *asoc,
/* Make peeled-off sockets more like 1-1 accepted sockets.
* Set the daddr and initialize id to something more random
*/
+ af = sctp_get_af_specific(asoc->peer.primary_addr.sa.sa_family);
+ af->to_sk_daddr(&asoc->peer.primary_addr, sk);
inetsk = inet_sk(sock->sk);
- inetsk->daddr = asoc->peer.primary_addr.v4.sin_addr.s_addr;
inetsk->id = asoc->next_tsn ^ jiffies;
*sockp = sock;
@@ -4343,11 +4355,12 @@ copy_getaddrs:
err = -EFAULT;
goto error;
}
- if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
- return -EFAULT;
+ if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) {
+ err = -EFAULT;
+ goto error;
+ }
if (put_user(bytes_copied, optlen))
- return -EFAULT;
-
+ err = -EFAULT;
error:
kfree(addrs);
return err;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 961df275d5b9..5f467c914f80 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -241,6 +241,45 @@ void sctp_transport_pmtu(struct sctp_transport *transport)
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
}
+/* this is a complete rip-off from __sk_dst_check
+ * the cookie is always 0 since this is how it's used in the
+ * pmtu code
+ */
+static struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t)
+{
+ struct dst_entry *dst = t->dst;
+
+ if (dst && dst->obsolete && dst->ops->check(dst, 0) == NULL) {
+ dst_release(t->dst);
+ t->dst = NULL;
+ return NULL;
+ }
+
+ return dst;
+}
+
+void sctp_transport_update_pmtu(struct sctp_transport *t, u32 pmtu)
+{
+ struct dst_entry *dst;
+
+ if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
+ printk(KERN_WARNING "%s: Reported pmtu %d too low, "
+ "using default minimum of %d\n",
+ __FUNCTION__, pmtu,
+ SCTP_DEFAULT_MINSEGMENT);
+ /* Use default minimum segment size and disable
+ * pmtu discovery on this transport.
+ */
+ t->pathmtu = SCTP_DEFAULT_MINSEGMENT;
+ } else {
+ t->pathmtu = pmtu;
+ }
+
+ dst = sctp_transport_dst_check(t);
+ if (dst)
+ dst->ops->update_pmtu(dst, pmtu);
+}
+
/* Caches the dst entry and source address for a transport's destination
* address.
*/