sctp: add dst_pending_confirm flag
authorJulian Anastasov <ja@ssi.bg>
Mon, 6 Feb 2017 21:14:13 +0000 (23:14 +0200)
committerDavid S. Miller <davem@davemloft.net>
Tue, 7 Feb 2017 18:07:46 +0000 (13:07 -0500)
Add new transport flag to allow sockets to confirm neighbour.
When same struct dst_entry can be used for many different
neighbours we can not use it for pending confirmations.
The flag is propagated from transport to every packet.
It is reset when cached dst is reset.

Reported-by: YueHaibing <yuehaibing@huawei.com>
Fixes: 5110effee8fd ("net: Do delayed neigh confirmation.")
Fixes: f2bb4bedf35d ("ipv4: Cache output routes in fib_info nexthops.")
Signed-off-by: Julian Anastasov <ja@ssi.bg>
Acked-by: Eric Dumazet <edumazet@google.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/sctp.h
include/net/sctp/structs.h
net/sctp/associola.c
net/sctp/output.c
net/sctp/outqueue.c
net/sctp/sm_make_chunk.c
net/sctp/sm_sideeffect.c
net/sctp/socket.c
net/sctp/transport.c

index 3cfd365bcfbc965476dad76b0c8c17cec92f55be..480b65a24aff1ccd9ed3b0698a819a09a897c15b 100644 (file)
@@ -593,10 +593,8 @@ static inline void sctp_v4_map_v6(union sctp_addr *addr)
  */
 static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t)
 {
-       if (t->dst && !dst_check(t->dst, t->dst_cookie)) {
-               dst_release(t->dst);
-               t->dst = NULL;
-       }
+       if (t->dst && !dst_check(t->dst, t->dst_cookie))
+               sctp_transport_dst_release(t);
 
        return t->dst;
 }
index 231fa9ac50bd9e9a4fdb78123671504a1cfaf949..6a685049f67fcf3301aeb250ac0baa8f99666aaf 100644 (file)
@@ -804,6 +804,8 @@ struct sctp_transport {
 
        __u32 burst_limited;    /* Holds old cwnd when max.burst is applied */
 
+       __u32 dst_pending_confirm;      /* need to confirm neighbour */
+
        /* Destination */
        struct dst_entry *dst;
        /* Source address. */
@@ -950,6 +952,8 @@ unsigned long sctp_transport_timeout(struct sctp_transport *);
 void sctp_transport_reset(struct sctp_transport *);
 void sctp_transport_update_pmtu(struct sock *, struct sctp_transport *, u32);
 void sctp_transport_immediate_rtx(struct sctp_transport *);
+void sctp_transport_dst_release(struct sctp_transport *t);
+void sctp_transport_dst_confirm(struct sctp_transport *t);
 
 
 /* This is the structure we use to queue packets as they come into
index e50dc6d7543fd6acfa7442f3a9ee575203c7718d..2a6835b4562b61cff52425a530524f1c48bc7919 100644 (file)
@@ -832,8 +832,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
                if (transport->state != SCTP_UNCONFIRMED)
                        transport->state = SCTP_INACTIVE;
                else {
-                       dst_release(transport->dst);
-                       transport->dst = NULL;
+                       sctp_transport_dst_release(transport);
                        ulp_notify = false;
                }
 
index 07ab5062e541dc663b619a278742425a92002945..814eac047467c5b9fe8fdb4dd6b21a57812b3e1f 100644 (file)
@@ -546,6 +546,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
        struct sctp_association *asoc = tp->asoc;
        struct sctp_chunk *chunk, *tmp;
        int pkt_count, gso = 0;
+       int confirm;
        struct dst_entry *dst;
        struct sk_buff *head;
        struct sctphdr *sh;
@@ -624,7 +625,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
                        asoc->peer.last_sent_to = tp;
        }
        head->ignore_df = packet->ipfragok;
-       tp->af_specific->sctp_xmit(head, tp);
+       confirm = tp->dst_pending_confirm;
+       if (confirm)
+               skb_set_dst_pending_confirm(head, 1);
+       /* neighbour should be confirmed on successful transmission or
+        * positive error
+        */
+       if (tp->af_specific->sctp_xmit(head, tp) >= 0 && confirm)
+               tp->dst_pending_confirm = 0;
 
 out:
        list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
index 65abe22d869167b13193e8b057572d9d8f84625c..db352e5d61f8980dc461a162959643d872997217 100644 (file)
@@ -1654,7 +1654,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
 
                if (forward_progress) {
                        if (transport->dst)
-                               dst_confirm(transport->dst);
+                               sctp_transport_dst_confirm(transport);
                }
        }
 
index ad3445b3408eac9791ee5645b55da68ee14e20b1..c7d3249f88ec1c6d451663d09b81bac2ae1acca9 100644 (file)
@@ -3333,8 +3333,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
                local_bh_enable();
                list_for_each_entry(transport, &asoc->peer.transport_addr_list,
                                transports) {
-                       dst_release(transport->dst);
-                       transport->dst = NULL;
+                       sctp_transport_dst_release(transport);
                }
                break;
        case SCTP_PARAM_DEL_IP:
@@ -3348,8 +3347,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
                local_bh_enable();
                list_for_each_entry(transport, &asoc->peer.transport_addr_list,
                                transports) {
-                       dst_release(transport->dst);
-                       transport->dst = NULL;
+                       sctp_transport_dst_release(transport);
                }
                break;
        default:
index a4552712b8820c5d6972b046cbfe1bee94aded4c..51abcc90fe75d47ab16d717102b82780022d88a7 100644 (file)
@@ -755,7 +755,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
         * forward progress.
         */
        if (t->dst)
-               dst_confirm(t->dst);
+               sctp_transport_dst_confirm(t);
 
        /* The receiver of the HEARTBEAT ACK should also perform an
         * RTT measurement for that destination transport address
index 5fc7122c76deb86c798059e62c72be33cf1e455d..a4609a0be76dd46ec7d1d9eabe5d72b8c1b841b3 100644 (file)
@@ -592,7 +592,7 @@ static int sctp_send_asconf_add_ip(struct sock              *sk,
                        list_for_each_entry(trans,
                            &asoc->peer.transport_addr_list, transports) {
                                /* Clear the source and route cache */
-                               dst_release(trans->dst);
+                               sctp_transport_dst_release(trans);
                                trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
                                    2*asoc->pathmtu, 4380));
                                trans->ssthresh = asoc->peer.i.a_rwnd;
@@ -843,7 +843,7 @@ skip_mkasconf:
                 */
                list_for_each_entry(transport, &asoc->peer.transport_addr_list,
                                        transports) {
-                       dst_release(transport->dst);
+                       sctp_transport_dst_release(transport);
                        sctp_transport_route(transport, NULL,
                                             sctp_sk(asoc->base.sk));
                }
index baa1ac00d7b56d3193980e7469f4582e9dc42566..5b63ceb3bf3758f441a3240d7b516c5e543bfc98 100644 (file)
@@ -240,7 +240,7 @@ void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
 {
        /* If we don't have a fresh route, look one up */
        if (!transport->dst || transport->dst->obsolete) {
-               dst_release(transport->dst);
+               sctp_transport_dst_release(transport);
                transport->af_specific->get_dst(transport, &transport->saddr,
                                                &transport->fl, sk);
        }
@@ -672,3 +672,17 @@ void sctp_transport_immediate_rtx(struct sctp_transport *t)
                        sctp_transport_hold(t);
        }
 }
+
+/* Drop dst */
+void sctp_transport_dst_release(struct sctp_transport *t)
+{
+       dst_release(t->dst);
+       t->dst = NULL;
+       t->dst_pending_confirm = 0;
+}
+
+/* Schedule neighbour confirm */
+void sctp_transport_dst_confirm(struct sctp_transport *t)
+{
+       t->dst_pending_confirm = 1;
+}