sctp: add dst_pending_confirm flag

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: 5110effee8 ("net: Do delayed neigh confirmation.")
Fixes: f2bb4bedf3 ("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>
This commit is contained in:
Julian Anastasov 2017-02-06 23:14:13 +02:00 committed by David S. Miller
parent 4ff0620354
commit c86a773c78
9 changed files with 37 additions and 16 deletions

View 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) static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t)
{ {
if (t->dst && !dst_check(t->dst, t->dst_cookie)) { if (t->dst && !dst_check(t->dst, t->dst_cookie))
dst_release(t->dst); sctp_transport_dst_release(t);
t->dst = NULL;
}
return t->dst; return t->dst;
} }

View File

@ -804,6 +804,8 @@ struct sctp_transport {
__u32 burst_limited; /* Holds old cwnd when max.burst is applied */ __u32 burst_limited; /* Holds old cwnd when max.burst is applied */
__u32 dst_pending_confirm; /* need to confirm neighbour */
/* Destination */ /* Destination */
struct dst_entry *dst; struct dst_entry *dst;
/* Source address. */ /* Source address. */
@ -950,6 +952,8 @@ unsigned long sctp_transport_timeout(struct sctp_transport *);
void sctp_transport_reset(struct sctp_transport *); void sctp_transport_reset(struct sctp_transport *);
void sctp_transport_update_pmtu(struct sock *, struct sctp_transport *, u32); void sctp_transport_update_pmtu(struct sock *, struct sctp_transport *, u32);
void sctp_transport_immediate_rtx(struct sctp_transport *); 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 /* This is the structure we use to queue packets as they come into

View File

@ -832,8 +832,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
if (transport->state != SCTP_UNCONFIRMED) if (transport->state != SCTP_UNCONFIRMED)
transport->state = SCTP_INACTIVE; transport->state = SCTP_INACTIVE;
else { else {
dst_release(transport->dst); sctp_transport_dst_release(transport);
transport->dst = NULL;
ulp_notify = false; ulp_notify = false;
} }

View File

@ -546,6 +546,7 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
struct sctp_association *asoc = tp->asoc; struct sctp_association *asoc = tp->asoc;
struct sctp_chunk *chunk, *tmp; struct sctp_chunk *chunk, *tmp;
int pkt_count, gso = 0; int pkt_count, gso = 0;
int confirm;
struct dst_entry *dst; struct dst_entry *dst;
struct sk_buff *head; struct sk_buff *head;
struct sctphdr *sh; struct sctphdr *sh;
@ -624,7 +625,14 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
asoc->peer.last_sent_to = tp; asoc->peer.last_sent_to = tp;
} }
head->ignore_df = packet->ipfragok; 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: out:
list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {

View File

@ -1654,7 +1654,7 @@ static void sctp_check_transmitted(struct sctp_outq *q,
if (forward_progress) { if (forward_progress) {
if (transport->dst) if (transport->dst)
dst_confirm(transport->dst); sctp_transport_dst_confirm(transport);
} }
} }

View File

@ -3333,8 +3333,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
local_bh_enable(); local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list, list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) { transports) {
dst_release(transport->dst); sctp_transport_dst_release(transport);
transport->dst = NULL;
} }
break; break;
case SCTP_PARAM_DEL_IP: case SCTP_PARAM_DEL_IP:
@ -3348,8 +3347,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
local_bh_enable(); local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list, list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) { transports) {
dst_release(transport->dst); sctp_transport_dst_release(transport);
transport->dst = NULL;
} }
break; break;
default: default:

View File

@ -755,7 +755,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
* forward progress. * forward progress.
*/ */
if (t->dst) if (t->dst)
dst_confirm(t->dst); sctp_transport_dst_confirm(t);
/* The receiver of the HEARTBEAT ACK should also perform an /* The receiver of the HEARTBEAT ACK should also perform an
* RTT measurement for that destination transport address * RTT measurement for that destination transport address

View File

@ -592,7 +592,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
list_for_each_entry(trans, list_for_each_entry(trans,
&asoc->peer.transport_addr_list, transports) { &asoc->peer.transport_addr_list, transports) {
/* Clear the source and route cache */ /* Clear the source and route cache */
dst_release(trans->dst); sctp_transport_dst_release(trans);
trans->cwnd = min(4*asoc->pathmtu, max_t(__u32, trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
2*asoc->pathmtu, 4380)); 2*asoc->pathmtu, 4380));
trans->ssthresh = asoc->peer.i.a_rwnd; trans->ssthresh = asoc->peer.i.a_rwnd;
@ -843,7 +843,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
*/ */
list_for_each_entry(transport, &asoc->peer.transport_addr_list, list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) { transports) {
dst_release(transport->dst); sctp_transport_dst_release(transport);
sctp_transport_route(transport, NULL, sctp_transport_route(transport, NULL,
sctp_sk(asoc->base.sk)); sctp_sk(asoc->base.sk));
} }

View 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 we don't have a fresh route, look one up */
if (!transport->dst || transport->dst->obsolete) { if (!transport->dst || transport->dst->obsolete) {
dst_release(transport->dst); sctp_transport_dst_release(transport);
transport->af_specific->get_dst(transport, &transport->saddr, transport->af_specific->get_dst(transport, &transport->saddr,
&transport->fl, sk); &transport->fl, sk);
} }
@ -672,3 +672,17 @@ void sctp_transport_immediate_rtx(struct sctp_transport *t)
sctp_transport_hold(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;
}