diff options
Diffstat (limited to 'net/sctp')
-rw-r--r-- | net/sctp/bind_addr.c | 10 | ||||
-rw-r--r-- | net/sctp/debug.c | 1 | ||||
-rw-r--r-- | net/sctp/endpointola.c | 20 | ||||
-rw-r--r-- | net/sctp/input.c | 19 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 185 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 19 | ||||
-rw-r--r-- | net/sctp/protocol.c | 71 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 62 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 9 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 89 | ||||
-rw-r--r-- | net/sctp/sm_statetable.c | 78 | ||||
-rw-r--r-- | net/sctp/socket.c | 95 | ||||
-rw-r--r-- | net/sctp/transport.c | 27 | ||||
-rw-r--r-- | net/sctp/ulpevent.c | 30 |
14 files changed, 396 insertions, 319 deletions
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 3c06c87cd28..6338413376c 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -140,14 +140,12 @@ void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port) /* Dispose of the address list. */ static void sctp_bind_addr_clean(struct sctp_bind_addr *bp) { - struct sctp_sockaddr_entry *addr; - struct list_head *pos, *temp; + struct sctp_sockaddr_entry *addr, *temp; /* Empty the bind address list. */ - list_for_each_safe(pos, temp, &bp->address_list) { - addr = list_entry(pos, struct sctp_sockaddr_entry, list); - list_del(pos); - kfree(addr); + list_for_each_entry_safe(addr, temp, &bp->address_list, list) { + list_del_rcu(&addr->list); + call_rcu(&addr->rcu, sctp_local_addr_free); SCTP_DBG_OBJCNT_DEC(addr); } } diff --git a/net/sctp/debug.c b/net/sctp/debug.c index bf24fa697de..ec997cfe0a7 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -98,7 +98,6 @@ const char *sctp_cname(const sctp_subtype_t cid) /* These are printable forms of the states. */ const char *const sctp_state_tbl[SCTP_STATE_NUM_STATES] = { - "STATE_EMPTY", "STATE_CLOSED", "STATE_COOKIE_WAIT", "STATE_COOKIE_ECHOED", diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index e10acc01c75..c8cc24e282c 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -325,6 +325,7 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( struct sctp_transport **transport) { struct sctp_association *asoc = NULL; + struct sctp_association *tmp; struct sctp_transport *t = NULL; struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -333,25 +334,32 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( int rport; *transport = NULL; + + /* If the local port is not set, there can't be any associations + * on this endpoint. + */ + if (!ep->base.bind_addr.port) + goto out; + rport = ntohs(paddr->v4.sin_port); hash = sctp_assoc_hashfn(ep->base.bind_addr.port, rport); head = &sctp_assoc_hashtable[hash]; read_lock(&head->lock); sctp_for_each_hentry(epb, node, &head->chain) { - asoc = sctp_assoc(epb); - if (asoc->ep != ep || rport != asoc->peer.port) - goto next; + tmp = sctp_assoc(epb); + if (tmp->ep != ep || rport != tmp->peer.port) + continue; - t = sctp_assoc_lookup_paddr(asoc, paddr); + t = sctp_assoc_lookup_paddr(tmp, paddr); if (t) { + asoc = tmp; *transport = t; break; } -next: - asoc = NULL; } read_unlock(&head->lock); +out: return asoc; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 5436c692116..741ed164883 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -565,7 +565,7 @@ void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) */ void sctp_v4_err(struct sk_buff *skb, __u32 info) { - struct iphdr *iph = (struct iphdr *)skb->data; + const struct iphdr *iph = (const struct iphdr *)skb->data; const int ihlen = iph->ihl * 4; const int type = icmp_hdr(skb)->type; const int code = icmp_hdr(skb)->code; @@ -661,7 +661,6 @@ static int sctp_rcv_ootb(struct sk_buff *skb) { sctp_chunkhdr_t *ch; __u8 *ch_end; - sctp_errhdr_t *err; ch = (sctp_chunkhdr_t *) skb->data; @@ -697,20 +696,6 @@ static int sctp_rcv_ootb(struct sk_buff *skb) if (SCTP_CID_INIT == ch->type && (void *)ch != skb->data) goto discard; - /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR - * or a COOKIE ACK the SCTP Packet should be silently - * discarded. - */ - if (SCTP_CID_COOKIE_ACK == ch->type) - goto discard; - - if (SCTP_CID_ERROR == ch->type) { - sctp_walk_errors(err, ch) { - if (SCTP_ERROR_STALE_COOKIE == err->cause) - goto discard; - } - } - ch = (sctp_chunkhdr_t *) ch_end; } while (ch_end < skb_tail_pointer(skb)); @@ -1017,7 +1002,7 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( /* Skip over the ADDIP header and find the Address parameter */ param = (union sctp_addr_param *)(asconf + 1); - af = sctp_get_af_specific(param_type2af(param->v4.param_hdr.type)); + af = sctp_get_af_specific(param_type2af(param->p.type)); if (unlikely(!af)) return NULL; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 185fe058db1..0bb0d7cb9f1 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -80,6 +80,13 @@ #include <asm/uaccess.h> +static inline int sctp_v6_addr_match_len(union sctp_addr *s1, + union sctp_addr *s2); +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, + __be16 port); +static int sctp_v6_cmp_addr(const union sctp_addr *addr1, + const union sctp_addr *addr2); + /* Event handler for inet6 address addition/deletion events. * The sctp_local_addr_list needs to be protocted by a spin lock since * multiple notifiers (say IPv4 and IPv6) may be running at the same @@ -240,37 +247,107 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) /* Returns the dst cache entry for the given source and destination ip * addresses. */ -static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + struct flowi *fl, struct sock *sk) { - struct dst_entry *dst; - struct flowi6 fl6; + struct sctp_association *asoc = t->asoc; + struct dst_entry *dst = NULL; + struct flowi6 *fl6 = &fl->u.ip6; + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *laddr; + union sctp_addr *baddr = NULL; + union sctp_addr *daddr = &t->ipaddr; + union sctp_addr dst_saddr; + __u8 matchlen = 0; + __u8 bmatchlen; + sctp_scope_t scope; - memset(&fl6, 0, sizeof(fl6)); - ipv6_addr_copy(&fl6.daddr, &daddr->v6.sin6_addr); + memset(fl6, 0, sizeof(struct flowi6)); + ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr); + fl6->fl6_dport = daddr->v6.sin6_port; + fl6->flowi6_proto = IPPROTO_SCTP; if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) - fl6.flowi6_oif = daddr->v6.sin6_scope_id; + fl6->flowi6_oif = daddr->v6.sin6_scope_id; + SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr); - SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6.daddr); + if (asoc) + fl6->fl6_sport = htons(asoc->base.bind_addr.port); if (saddr) { - ipv6_addr_copy(&fl6.saddr, &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6.saddr); + ipv6_addr_copy(&fl6->saddr, &saddr->v6.sin6_addr); + fl6->fl6_sport = saddr->v6.sin6_port; + SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); + } + + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); + if (!asoc || saddr) + goto out; + + bp = &asoc->base.bind_addr; + scope = sctp_scope(daddr); + /* ip6_dst_lookup has filled in the fl6->saddr for us. Check + * to see if we can use it. + */ + if (!IS_ERR(dst)) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. + */ + sctp_v6_to_addr(&dst_saddr, &fl6->saddr, htons(bp->port)); + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) + continue; + + /* Do not compare against v4 addrs */ + if ((laddr->a.sa.sa_family == AF_INET6) && + (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { + rcu_read_unlock(); + goto out; + } + } + rcu_read_unlock(); + /* None of the bound addresses match the source address of the + * dst. So release it. + */ + dst_release(dst); + dst = NULL; } - dst = ip6_route_output(&init_net, NULL, &fl6); - if (!dst->error) { + /* Walk through the bind address list and try to get the + * best source address for a given destination. + */ + rcu_read_lock(); + list_for_each_entry_rcu(laddr, &bp->address_list, list) { + if (!laddr->valid && laddr->state != SCTP_ADDR_SRC) + continue; + if ((laddr->a.sa.sa_family == AF_INET6) && + (scope <= sctp_scope(&laddr->a))) { + bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); + if (!baddr || (matchlen < bmatchlen)) { + baddr = &laddr->a; + matchlen = bmatchlen; + } + } + } + rcu_read_unlock(); + if (baddr) { + ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr); + fl6->fl6_sport = baddr->v6.sin6_port; + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); + } + +out: + if (!IS_ERR(dst)) { struct rt6_info *rt; rt = (struct rt6_info *)dst; + t->dst = dst; SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", - &rt->rt6i_dst.addr, &rt->rt6i_src.addr); - return dst; + &rt->rt6i_dst.addr, &fl6->saddr); + } else { + t->dst = NULL; + SCTP_DEBUG_PRINTK("NO ROUTE\n"); } - SCTP_DEBUG_PRINTK("NO ROUTE\n"); - dst_release(dst); - return NULL; } /* Returns the number of consecutive initial bits that match in the 2 ipv6 @@ -286,64 +363,18 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, * and asoc's bind address list. */ static void sctp_v6_get_saddr(struct sctp_sock *sk, - struct sctp_association *asoc, - struct dst_entry *dst, - union sctp_addr *daddr, - union sctp_addr *saddr) + struct sctp_transport *t, + struct flowi *fl) { - struct sctp_bind_addr *bp; - struct sctp_sockaddr_entry *laddr; - sctp_scope_t scope; - union sctp_addr *baddr = NULL; - __u8 matchlen = 0; - __u8 bmatchlen; + struct flowi6 *fl6 = &fl->u.ip6; + union sctp_addr *saddr = &t->saddr; - SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ", - __func__, asoc, dst, &daddr->v6.sin6_addr); - - if (!asoc) { - ipv6_dev_get_saddr(sock_net(sctp_opt2sk(sk)), - dst ? ip6_dst_idev(dst)->dev : NULL, - &daddr->v6.sin6_addr, - inet6_sk(&sk->inet.sk)->srcprefs, - &saddr->v6.sin6_addr); - SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: %pI6\n", - &saddr->v6.sin6_addr); - return; - } - - scope = sctp_scope(daddr); - - bp = &asoc->base.bind_addr; + SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); - /* Go through the bind address list and find the best source address - * that matches the scope of the destination address. - */ - rcu_read_lock(); - list_for_each_entry_rcu(laddr, &bp->address_list, list) { - if (!laddr->valid) - continue; - if ((laddr->state == SCTP_ADDR_SRC) && - (laddr->a.sa.sa_family == AF_INET6) && - (scope <= sctp_scope(&laddr->a))) { - bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a); - if (!baddr || (matchlen < bmatchlen)) { - baddr = &laddr->a; - matchlen = bmatchlen; - } - } - } - - if (baddr) { - memcpy(saddr, baddr, sizeof(union sctp_addr)); - SCTP_DEBUG_PRINTK("saddr: %pI6\n", &saddr->v6.sin6_addr); - } else { - pr_err("%s: asoc:%p Could not find a valid source " - "address for the dest:%pI6\n", - __func__, asoc, &daddr->v6.sin6_addr); + if (t->dst) { + saddr->v6.sin6_family = AF_INET6; + ipv6_addr_copy(&saddr->v6.sin6_addr, &fl6->saddr); } - - rcu_read_unlock(); } /* Make a copy of all potential local addresses. */ @@ -465,14 +496,13 @@ static int sctp_v6_to_addr_param(const union sctp_addr *addr, return length; } -/* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, +/* Initialize a sctp_addr from struct in6_addr. */ +static void sctp_v6_to_addr(union sctp_addr *addr, struct in6_addr *saddr, __be16 port) { - struct rt6_info *rt = (struct rt6_info *)dst; addr->sa.sa_family = AF_INET6; addr->v6.sin6_port = port; - ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); + ipv6_addr_copy(&addr->v6.sin6_addr, saddr); } /* Compare addresses exactly. @@ -531,7 +561,7 @@ static int sctp_v6_is_any(const union sctp_addr *addr) static int sctp_v6_available(union sctp_addr *addr, struct sctp_sock *sp) { int type; - struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr; + const struct in6_addr *in6 = (const struct in6_addr *)&addr->v6.sin6_addr; type = ipv6_addr_type(in6); if (IPV6_ADDR_ANY == type) @@ -959,7 +989,6 @@ static struct sctp_af sctp_af_inet6 = { .to_sk_daddr = sctp_v6_to_sk_daddr, .from_addr_param = sctp_v6_from_addr_param, .to_addr_param = sctp_v6_to_addr_param, - .dst_saddr = sctp_v6_dst_saddr, .cmp_addr = sctp_v6_cmp_addr, .scope = sctp_v6_scope, .addr_valid = sctp_v6_addr_valid, diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index bf92a5b68f8..1c88c8911dc 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -131,7 +131,8 @@ static inline int sctp_cacc_skip_3_1_d(struct sctp_transport *primary, static inline int sctp_cacc_skip_3_1_f(struct sctp_transport *transport, int count_of_newacks) { - if (count_of_newacks < 2 && !transport->cacc.cacc_saw_newack) + if (count_of_newacks < 2 && + (transport && !transport->cacc.cacc_saw_newack)) return 1; return 0; } @@ -319,7 +320,6 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) * chunk. */ switch (q->asoc->state) { - case SCTP_STATE_EMPTY: case SCTP_STATE_CLOSED: case SCTP_STATE_SHUTDOWN_PENDING: case SCTP_STATE_SHUTDOWN_SENT: @@ -577,6 +577,13 @@ static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, * try to send as much as possible. */ list_for_each_entry_safe(chunk, chunk1, lqueue, transmitted_list) { + /* If the chunk is abandoned, move it to abandoned list. */ + if (sctp_chunk_abandoned(chunk)) { + list_del_init(&chunk->transmitted_list); + sctp_insert_list(&q->abandoned, + &chunk->transmitted_list); + continue; + } /* Make sure that Gap Acked TSNs are not retransmitted. A * simple approach is just to move such TSNs out of the @@ -618,9 +625,12 @@ redo: /* If we are retransmitting, we should only * send a single packet. + * Otherwise, try appending this chunk again. */ if (rtx_timeout || fast_rtx) done = 1; + else + goto redo; /* Bundle next chunk in the next round. */ break; @@ -1683,8 +1693,9 @@ static void sctp_mark_missing(struct sctp_outq *q, /* SFR-CACC may require us to skip marking * this chunk as missing. */ - if (!transport || !sctp_cacc_skip(primary, transport, - count_of_newacks, tsn)) { + if (!transport || !sctp_cacc_skip(primary, + chunk->transport, + count_of_newacks, tsn)) { chunk->tsn_missing_report++; SCTP_DEBUG_PRINTK( diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 065d99958ce..67380a29e2e 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -332,13 +332,12 @@ static int sctp_v4_to_addr_param(const union sctp_addr *addr, } /* Initialize a sctp_addr from a dst_entry. */ -static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst, +static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct flowi4 *fl4, __be16 port) { - struct rtable *rt = (struct rtable *)dst; saddr->v4.sin_family = AF_INET; saddr->v4.sin_port = port; - saddr->v4.sin_addr.s_addr = rt->rt_src; + saddr->v4.sin_addr.s_addr = fl4->saddr; } /* Compare two addresses exactly. */ @@ -456,35 +455,36 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) * addresses. If an association is passed, trys to get a dst entry with a * source address that matches an address in the bind address list. */ -static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + struct flowi *fl, struct sock *sk) { + struct sctp_association *asoc = t->asoc; struct rtable *rt; - struct flowi4 fl4; + struct flowi4 *fl4 = &fl->u.ip4; struct sctp_bind_addr *bp; struct sctp_sockaddr_entry *laddr; struct dst_entry *dst = NULL; + union sctp_addr *daddr = &t->ipaddr; union sctp_addr dst_saddr; - memset(&fl4, 0x0, sizeof(struct flowi4)); - fl4.daddr = daddr->v4.sin_addr.s_addr; - fl4.fl4_dport = daddr->v4.sin_port; - fl4.flowi4_proto = IPPROTO_SCTP; + memset(fl4, 0x0, sizeof(struct flowi4)); + fl4->daddr = daddr->v4.sin_addr.s_addr; + fl4->fl4_dport = daddr->v4.sin_port; + fl4->flowi4_proto = IPPROTO_SCTP; if (asoc) { - fl4.flowi4_tos = RT_CONN_FLAGS(asoc->base.sk); - fl4.flowi4_oif = asoc->base.sk->sk_bound_dev_if; - fl4.fl4_sport = htons(asoc->base.bind_addr.port); + fl4->flowi4_tos = RT_CONN_FLAGS(asoc->base.sk); + fl4->flowi4_oif = asoc->base.sk->sk_bound_dev_if; + fl4->fl4_sport = htons(asoc->base.bind_addr.port); } if (saddr) { - fl4.saddr = saddr->v4.sin_addr.s_addr; - fl4.fl4_sport = saddr->v4.sin_port; + fl4->saddr = saddr->v4.sin_addr.s_addr; + fl4->fl4_sport = saddr->v4.sin_port; } SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", - __func__, &fl4.daddr, &fl4.saddr); + __func__, &fl4->daddr, &fl4->saddr); - rt = ip_route_output_key(&init_net, &fl4); + rt = ip_route_output_key(&init_net, fl4); if (!IS_ERR(rt)) dst = &rt->dst; @@ -500,7 +500,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, /* Walk through the bind address list and look for a bind * address that matches the source address of the returned dst. */ - sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); + sctp_v4_dst_saddr(&dst_saddr, fl4, htons(bp->port)); rcu_read_lock(); list_for_each_entry_rcu(laddr, &bp->address_list, list) { if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC)) @@ -526,9 +526,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, continue; if ((laddr->state == SCTP_ADDR_SRC) && (AF_INET == laddr->a.sa.sa_family)) { - fl4.saddr = laddr->a.v4.sin_addr.s_addr; - fl4.fl4_sport = laddr->a.v4.sin_port; - rt = ip_route_output_key(&init_net, &fl4); + fl4->saddr = laddr->a.v4.sin_addr.s_addr; + fl4->fl4_sport = laddr->a.v4.sin_port; + rt = ip_route_output_key(&init_net, fl4); if (!IS_ERR(rt)) { dst = &rt->dst; goto out_unlock; @@ -539,33 +539,27 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, out_unlock: rcu_read_unlock(); out: + t->dst = dst; if (dst) SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n", - &rt->rt_dst, &rt->rt_src); + &fl4->daddr, &fl4->saddr); else SCTP_DEBUG_PRINTK("NO ROUTE\n"); - - return dst; } /* For v4, the source address is cached in the route entry(dst). So no need * to cache it separately and hence this is an empty routine. */ static void sctp_v4_get_saddr(struct sctp_sock *sk, - struct sctp_association *asoc, - struct dst_entry *dst, - union sctp_addr *daddr, - union sctp_addr *saddr) + struct sctp_transport *t, + struct flowi *fl) { - struct rtable *rt = (struct rtable *)dst; - - if (!asoc) - return; + union sctp_addr *saddr = &t->saddr; + struct rtable *rt = (struct rtable *)t->dst; if (rt) { saddr->v4.sin_family = AF_INET; - saddr->v4.sin_port = htons(asoc->base.bind_addr.port); - saddr->v4.sin_addr.s_addr = rt->rt_src; + saddr->v4.sin_addr.s_addr = fl->u.ip4.saddr; } } @@ -847,14 +841,14 @@ 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(skb)->rt_src, - &skb_rtable(skb)->rt_dst); + &transport->fl.u.ip4.saddr, + &transport->fl.u.ip4.daddr); inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; SCTP_INC_STATS(SCTP_MIB_OUTSCTPPACKS); - return ip_queue_xmit(skb); + return ip_queue_xmit(skb, &transport->fl); } static struct sctp_af sctp_af_inet; @@ -943,7 +937,6 @@ static struct sctp_af sctp_af_inet = { .to_sk_daddr = sctp_v4_to_sk_daddr, .from_addr_param = sctp_v4_from_addr_param, .to_addr_param = sctp_v4_to_addr_param, - .dst_saddr = sctp_v4_dst_saddr, .cmp_addr = sctp_v4_cmp_addr, .addr_valid = sctp_v4_addr_valid, .inaddr_any = sctp_v4_inaddr_any, diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index b3434cc7d0c..58eb27fed4b 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1075,20 +1075,28 @@ nodata: /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, - const struct sctp_transport *transport, - const void *payload, const size_t paylen) + const struct sctp_transport *transport) { - struct sctp_chunk *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, - 0, paylen); + struct sctp_chunk *retval; + sctp_sender_hb_info_t hbinfo; + + retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo)); if (!retval) goto nodata; + hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; + hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.daddr = transport->ipaddr; + hbinfo.sent_at = jiffies; + hbinfo.hb_nonce = transport->hb_nonce; + /* Cast away the 'const', as this is just telling the chunk * what transport it belongs to. */ retval->transport = (struct sctp_transport *) transport; - retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload); + retval->subh.hbs_hdr = sctp_addto_chunk(retval, sizeof(hbinfo), + &hbinfo); nodata: return retval; @@ -2242,14 +2250,17 @@ int sctp_verify_init(const struct sctp_association *asoc, * Returns 0 on failure, else success. * FIXME: This is an association method. */ -int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, +int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, const union sctp_addr *peer_addr, sctp_init_chunk_t *peer_init, gfp_t gfp) { union sctp_params param; struct sctp_transport *transport; struct list_head *pos, *temp; + struct sctp_af *af; + union sctp_addr addr; char *cookie; + int src_match = 0; /* We must include the address that the INIT packet came from. * This is the only address that matters for an INIT packet. @@ -2261,18 +2272,31 @@ int sctp_process_init(struct sctp_association *asoc, sctp_cid_t cid, * added as the primary transport. The source address seems to * be a a better choice than any of the embedded addresses. */ - if (peer_addr) { - if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) - goto nomem; - } + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp, SCTP_ACTIVE)) + goto nomem; + + if (sctp_cmp_addr_exact(sctp_source(chunk), peer_addr)) + src_match = 1; /* Process the initialization parameters. */ sctp_walk_params(param, peer_init, init_hdr.params) { + if (!src_match && (param.p->type == SCTP_PARAM_IPV4_ADDRESS || + param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { + af = sctp_get_af_specific(param_type2af(param.p->type)); + af->from_addr_param(&addr, param.addr, + chunk->sctp_hdr->source, 0); + if (sctp_cmp_addr_exact(sctp_source(chunk), &addr)) + src_match = 1; + } if (!sctp_process_param(asoc, param, peer_addr, gfp)) goto clean_up; } + /* source address of chunk may not match any valid address */ + if (!src_match) + goto clean_up; + /* AUTH: After processing the parameters, make sure that we * have all the required info to potentially do authentications. */ @@ -2923,7 +2947,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, asconf_param->param_hdr.type != SCTP_PARAM_SET_PRIMARY) return SCTP_ERROR_UNKNOWN_PARAM; - switch (addr_param->v4.param_hdr.type) { + switch (addr_param->p.type) { case SCTP_PARAM_IPV6_ADDRESS: if (!asoc->peer.ipv6_address) return SCTP_ERROR_DNS_FAILED; @@ -2936,7 +2960,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, return SCTP_ERROR_DNS_FAILED; } - af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); + af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (unlikely(!af)) return SCTP_ERROR_DNS_FAILED; @@ -3100,7 +3124,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, /* Skip the address parameter and store a pointer to the first * asconf parameter. */ - length = ntohs(addr_param->v4.param_hdr.length); + length = ntohs(addr_param->p.length); asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); chunk_len -= length; @@ -3177,7 +3201,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, ((void *)asconf_param + sizeof(sctp_addip_param_t)); /* We have checked the packet before, so we do not check again. */ - af = sctp_get_af_specific(param_type2af(addr_param->v4.param_hdr.type)); + af = sctp_get_af_specific(param_type2af(addr_param->p.type)); af->from_addr_param(&addr, addr_param, htons(bp->port), 0); switch (asconf_param->param_hdr.type) { @@ -3193,11 +3217,8 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, local_bh_enable(); list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { - if (transport->state == SCTP_ACTIVE) - continue; dst_release(transport->dst); - sctp_transport_route(transport, NULL, - sctp_sk(asoc->base.sk)); + transport->dst = NULL; } break; case SCTP_PARAM_DEL_IP: @@ -3207,8 +3228,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, list_for_each_entry(transport, &asoc->peer.transport_addr_list, transports) { dst_release(transport->dst); - sctp_transport_route(transport, NULL, - sctp_sk(asoc->base.sk)); + transport->dst = NULL; } break; default: @@ -3304,7 +3324,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, /* Skip the address parameter in the last asconf sent and store a * pointer to the first asconf parameter. */ - length = ntohs(addr_param->v4.param_hdr.length); + length = ntohs(addr_param->p.length); asconf_param = (sctp_addip_param_t *)((void *)addr_param + length); asconf_len -= length; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 5f86ee4b54c..d612ca1ca6c 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -595,8 +595,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, * fail during INIT processing (due to malloc problems), * just return the error and stop processing the stack. */ - if (!sctp_process_init(asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, gfp)) + if (!sctp_process_init(asoc, chunk, sctp_source(chunk), peer_init, gfp)) error = -ENOMEM; else error = 0; @@ -1415,12 +1414,6 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, SCTP_RTXR_T3_RTX); break; - case SCTP_CMD_TRANSMIT: - /* Kick start transmission. */ - error = sctp_outq_uncork(&asoc->outqueue); - local_cork = 0; - break; - case SCTP_CMD_ECN_CE: /* Do delayed CE processing. */ sctp_do_ecn_ce_work(asoc, cmd->obj.u32); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 76792083c37..7f4a4f8368e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -393,8 +393,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, goto nomem_init; /* The call, sctp_process_init(), can fail on memory allocation. */ - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC)) goto nomem_init; @@ -725,7 +724,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, + if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) goto nomem_init; @@ -942,18 +941,9 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, { struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_chunk *reply; - sctp_sender_hb_info_t hbinfo; - size_t paylen = 0; - - hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; - hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); - hbinfo.daddr = transport->ipaddr; - hbinfo.sent_at = jiffies; - hbinfo.hb_nonce = transport->hb_nonce; /* Send a heartbeat to our peer. */ - paylen = sizeof(sctp_sender_hb_info_t); - reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen); + reply = sctp_make_heartbeat(asoc, transport); if (!reply) return SCTP_DISPOSITION_NOMEM; @@ -1464,8 +1454,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( * Verification Tag and Peers Verification tag into a reserved * place (local tie-tag and per tie-tag) within the state cookie. */ - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC)) goto nomem; @@ -1694,8 +1683,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const struct sctp_endpoint *ep, */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init, GFP_ATOMIC)) goto nomem; @@ -1780,8 +1768,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const struct sctp_endpoint *ep, * side effects--it is safe to run them here. */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; - if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, + if (!sctp_process_init(new_asoc, chunk, sctp_source(chunk), peer_init, GFP_ATOMIC)) goto nomem; @@ -2412,8 +2399,15 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); - if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) + if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) { + + sctp_errhdr_t *err; + sctp_walk_errors(err, chunk->chunk_hdr); + if ((void *)err != (void *)chunk->chunk_end) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + } sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); /* ASSOC_FAILED will DELETE_TCB. */ @@ -3204,6 +3198,7 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; + sctp_errhdr_t *err; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -3212,6 +3207,10 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) return sctp_sf_violation_chunklen(ep, asoc, type, arg, commands); + sctp_walk_errors(err, chunk->chunk_hdr); + if ((void *)err != (void *)chunk->chunk_end) + return sctp_sf_violation_paramlen(ep, asoc, type, arg, + (void *)err, commands); sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_OPERR, SCTP_CHUNK(chunk)); @@ -3320,8 +3319,10 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; sctp_chunkhdr_t *ch; + sctp_errhdr_t *err; __u8 *ch_end; int ootb_shut_ack = 0; + int ootb_cookie_ack = 0; SCTP_INC_STATS(SCTP_MIB_OUTOFBLUES); @@ -3346,6 +3347,23 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, if (SCTP_CID_ABORT == ch->type) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR + * or a COOKIE ACK the SCTP Packet should be silently + * discarded. + */ + + if (SCTP_CID_COOKIE_ACK == ch->type) + ootb_cookie_ack = 1; + + if (SCTP_CID_ERROR == ch->type) { + sctp_walk_errors(err, ch) { + if (SCTP_ERROR_STALE_COOKIE == err->cause) { + ootb_cookie_ack = 1; + break; + } + } + } + /* Report violation if chunk len overflows */ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); if (ch_end > skb_tail_pointer(skb)) @@ -3357,6 +3375,8 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, if (ootb_shut_ack) return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + else if (ootb_cookie_ack) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); else return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } @@ -4343,8 +4363,9 @@ static sctp_disposition_t sctp_sf_violation_chunklen( /* * Handle a protocol violation when the parameter length is invalid. - * "Invalid" length is identified as smaller than the minimal length a - * given parameter can be. + * If the length is smaller than the minimum length of a given parameter, + * or accumulated length in multi parameters exceeds the end of the chunk, + * the length is considered as invalid. */ static sctp_disposition_t sctp_sf_violation_paramlen( const struct sctp_endpoint *ep, @@ -5056,6 +5077,30 @@ sctp_disposition_t sctp_sf_ignore_primitive( ***************************************************************************/ /* + * When the SCTP stack has no more user data to send or retransmit, this + * notification is given to the user. Also, at the time when a user app + * subscribes to this event, if there is no data to be sent or + * retransmit, the stack will immediately send up this notification. + */ +sctp_disposition_t sctp_sf_do_no_pending_tsn( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_ulpevent *event; + + event = sctp_ulpevent_make_sender_dry_event(asoc, GFP_ATOMIC); + if (!event) + return SCTP_DISPOSITION_NOMEM; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(event)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* * Start the shutdown negotiation. * * From Section 9.2: diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 546d4387fb3..0338dc6fdc9 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -107,8 +107,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, #define TYPE_SCTP_FUNC(func) {.fn = func, .name = #func} #define TYPE_SCTP_DATA { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -128,8 +126,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_DATA */ #define TYPE_SCTP_INIT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_1B_init), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -149,8 +145,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_INIT */ #define TYPE_SCTP_INIT_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_2_3_initack), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -170,8 +164,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_INIT_ACK */ #define TYPE_SCTP_SACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -191,8 +183,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SACK */ #define TYPE_SCTP_HEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -213,8 +203,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_HEARTBEAT */ #define TYPE_SCTP_HEARTBEAT_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -234,8 +222,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_HEARTBEAT_ACK */ #define TYPE_SCTP_ABORT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_pdiscard), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -255,8 +241,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ABORT */ #define TYPE_SCTP_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -276,8 +260,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SHUTDOWN */ #define TYPE_SCTP_SHUTDOWN_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -297,8 +279,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_SHUTDOWN_ACK */ #define TYPE_SCTP_ERROR { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -318,8 +298,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ERROR */ #define TYPE_SCTP_COOKIE_ECHO { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_5_1D_ce), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -339,8 +317,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_COOKIE_ECHO */ #define TYPE_SCTP_COOKIE_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -360,8 +336,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_COOKIE_ACK */ #define TYPE_SCTP_ECN_ECNE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -381,8 +355,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ECN_ECNE */ #define TYPE_SCTP_ECN_CWR { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -402,8 +374,6 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, } /* TYPE_SCTP_ECN_CWR */ #define TYPE_SCTP_SHUTDOWN_COMPLETE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -446,8 +416,6 @@ static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][ }; /* state_fn_t chunk_event_table[][] */ #define TYPE_SCTP_ASCONF { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -467,8 +435,6 @@ static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][ } /* TYPE_SCTP_ASCONF */ #define TYPE_SCTP_ASCONF_ACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -496,8 +462,6 @@ static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_ }; /*state_fn_t addip_chunk_event_table[][] */ #define TYPE_SCTP_FWD_TSN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -524,8 +488,6 @@ static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUN }; /*state_fn_t prsctp_chunk_event_table[][] */ #define TYPE_SCTP_AUTH { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -553,8 +515,6 @@ static const sctp_sm_table_entry_t auth_chunk_event_table[SCTP_NUM_AUTH_CHUNK_TY static const sctp_sm_table_entry_t chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { - /* SCTP_STATE_EMPTY */ - TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_CLOSED */ TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_COOKIE_WAIT */ @@ -575,8 +535,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { #define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_do_prm_asoc), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -596,8 +554,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */ #define TYPE_SCTP_PRIMITIVE_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -617,8 +573,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */ #define TYPE_SCTP_PRIMITIVE_ABORT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -638,8 +592,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_ABORT */ #define TYPE_SCTP_PRIMITIVE_SEND { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -659,8 +611,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_SEND */ #define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -680,8 +630,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { } /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */ #define TYPE_SCTP_PRIMITIVE_ASCONF { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_error_closed), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -713,8 +661,6 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE }; #define TYPE_SCTP_OTHER_NO_PENDING_TSN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -722,7 +668,7 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE /* SCTP_STATE_COOKIE_ECHOED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_ESTABLISHED */ \ - TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ + TYPE_SCTP_FUNC(sctp_sf_do_no_pending_tsn), \ /* SCTP_STATE_SHUTDOWN_PENDING */ \ TYPE_SCTP_FUNC(sctp_sf_do_9_2_start_shutdown), \ /* SCTP_STATE_SHUTDOWN_SENT */ \ @@ -734,8 +680,6 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE } #define TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -760,8 +704,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ }; #define TYPE_SCTP_EVENT_TIMEOUT_NONE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -781,8 +723,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -802,8 +742,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -823,8 +761,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -844,8 +780,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -865,8 +799,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T4_RTO { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -886,8 +818,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -907,8 +837,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -928,8 +856,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_SACK { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_bug), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ @@ -949,8 +875,6 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ } #define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \ - /* SCTP_STATE_EMPTY */ \ - TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_CLOSED */ \ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ /* SCTP_STATE_COOKIE_WAIT */ \ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index deb82e35a10..6766913a53e 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -658,11 +658,15 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt) goto err_bindx_rem; } - if (sa_addr->v4.sin_port != htons(bp->port)) { + if (sa_addr->v4.sin_port && + sa_addr->v4.sin_port != htons(bp->port)) { retval = -EINVAL; goto err_bindx_rem; } + if (!sa_addr->v4.sin_port) + sa_addr->v4.sin_port = htons(bp->port); + /* FIXME - There is probably a need to check if sk->sk_saddr and * sk->sk_rcv_addr are currently set to one of the addresses to * be removed. This is something which needs to be looked into @@ -1492,7 +1496,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, struct sctp_chunk *chunk; union sctp_addr to; struct sockaddr *msg_name = NULL; - struct sctp_sndrcvinfo default_sinfo = { 0 }; + struct sctp_sndrcvinfo default_sinfo; struct sctp_sndrcvinfo *sinfo; struct sctp_initmsg *sinit; sctp_assoc_t associd = 0; @@ -1756,6 +1760,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* If the user didn't specify SNDRCVINFO, make up one with * some defaults. */ + memset(&default_sinfo, 0, sizeof(default_sinfo)); default_sinfo.sinfo_stream = asoc->default_stream; default_sinfo.sinfo_flags = asoc->default_flags; default_sinfo.sinfo_ppid = asoc->default_ppid; @@ -1786,12 +1791,10 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, goto out_free; } - if (sinfo) { - /* Check for invalid stream. */ - if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { - err = -EINVAL; - goto out_free; - } + /* Check for invalid stream. */ + if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) { + err = -EINVAL; + goto out_free; } timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); @@ -2283,7 +2286,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, trans->param_flags = (trans->param_flags & ~SPP_PMTUD) | pmtud_change; if (update) { - sctp_transport_pmtu(trans); + sctp_transport_pmtu(trans, sctp_opt2sk(sp)); sctp_assoc_sync_pmtu(asoc); } } else if (asoc) { @@ -3215,14 +3218,9 @@ static int sctp_setsockopt_hmac_ident(struct sock *sk, if (optlen < sizeof(struct sctp_hmacalgo)) return -EINVAL; - hmacs = kmalloc(optlen, GFP_KERNEL); - if (!hmacs) - return -ENOMEM; - - if (copy_from_user(hmacs, optval, optlen)) { - err = -EFAULT; - goto out; - } + hmacs= memdup_user(optval, optlen); + if (IS_ERR(hmacs)) + return PTR_ERR(hmacs); idents = hmacs->shmac_num_idents; if (idents == 0 || idents > SCTP_AUTH_NUM_HMACS || @@ -3257,14 +3255,9 @@ static int sctp_setsockopt_auth_key(struct sock *sk, if (optlen <= sizeof(struct sctp_authkey)) return -EINVAL; - authkey = kmalloc(optlen, GFP_KERNEL); - if (!authkey) - return -ENOMEM; - - if (copy_from_user(authkey, optval, optlen)) { - ret = -EFAULT; - goto out; - } + authkey= memdup_user(optval, optlen); + if (IS_ERR(authkey)) + return PTR_ERR(authkey); if (authkey->sca_keylength > optlen - sizeof(struct sctp_authkey)) { ret = -EINVAL; @@ -5283,6 +5276,55 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len, return 0; } +/* + * 8.2.6. Get the Current Identifiers of Associations + * (SCTP_GET_ASSOC_ID_LIST) + * + * This option gets the current list of SCTP association identifiers of + * the SCTP associations handled by a one-to-many style socket. + */ +static int sctp_getsockopt_assoc_ids(struct sock *sk, int len, + char __user *optval, int __user *optlen) +{ + struct sctp_sock *sp = sctp_sk(sk); + struct sctp_association *asoc; + struct sctp_assoc_ids *ids; + u32 num = 0; + + if (sctp_style(sk, TCP)) + return -EOPNOTSUPP; + + if (len < sizeof(struct sctp_assoc_ids)) + return -EINVAL; + + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + num++; + } + + if (len < sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num) + return -EINVAL; + + len = sizeof(struct sctp_assoc_ids) + sizeof(sctp_assoc_t) * num; + + ids = kmalloc(len, GFP_KERNEL); + if (unlikely(!ids)) + return -ENOMEM; + + ids->gaids_number_of_ids = num; + num = 0; + list_for_each_entry(asoc, &(sp->ep->asocs), asocs) { + ids->gaids_assoc_id[num++] = asoc->assoc_id; + } + + if (put_user(len, optlen) || copy_to_user(optval, ids, len)) { + kfree(ids); + return -EFAULT; + } + + kfree(ids); + return 0; +} + SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -5415,6 +5457,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, case SCTP_GET_ASSOC_NUMBER: retval = sctp_getsockopt_assoc_number(sk, len, optval, optlen); break; + case SCTP_GET_ASSOC_ID_LIST: + retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index d3ae493d234..394c57ca2f5 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -211,15 +211,17 @@ void sctp_transport_set_owner(struct sctp_transport *transport, } /* Initialize the pmtu of a transport. */ -void sctp_transport_pmtu(struct sctp_transport *transport) +void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk) { - struct dst_entry *dst; - - dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL); + /* If we don't have a fresh route, look one up */ + if (!transport->dst || transport->dst->obsolete > 1) { + dst_release(transport->dst); + transport->af_specific->get_dst(transport, &transport->saddr, + &transport->fl, sk); + } - if (dst) { - transport->pathmtu = dst_mtu(dst); - dst_release(dst); + if (transport->dst) { + transport->pathmtu = dst_mtu(transport->dst); } else transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT; } @@ -270,22 +272,19 @@ void sctp_transport_route(struct sctp_transport *transport, { struct sctp_association *asoc = transport->asoc; struct sctp_af *af = transport->af_specific; - union sctp_addr *daddr = &transport->ipaddr; - struct dst_entry *dst; - dst = af->get_dst(asoc, daddr, saddr); + af->get_dst(transport, saddr, &transport->fl, sctp_opt2sk(opt)); if (saddr) memcpy(&transport->saddr, saddr, sizeof(union sctp_addr)); else - af->get_saddr(opt, asoc, dst, daddr, &transport->saddr); + af->get_saddr(opt, transport, &transport->fl); - transport->dst = dst; if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) { return; } - if (dst) { - transport->pathmtu = dst_mtu(dst); + if (transport->dst) { + transport->pathmtu = dst_mtu(transport->dst); /* Initialize sk->sk_rcv_saddr, if the transport is the * association's active path for getsockname(). diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 61b1f5ada96..e70e5fc8789 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -843,7 +843,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_authkey( ak = (struct sctp_authkey_event *) skb_put(skb, sizeof(struct sctp_authkey_event)); - ak->auth_type = SCTP_AUTHENTICATION_INDICATION; + ak->auth_type = SCTP_AUTHENTICATION_EVENT; ak->auth_flags = 0; ak->auth_length = sizeof(struct sctp_authkey_event); @@ -862,6 +862,34 @@ fail: return NULL; } +/* + * Socket Extensions for SCTP + * 6.3.10. SCTP_SENDER_DRY_EVENT + */ +struct sctp_ulpevent *sctp_ulpevent_make_sender_dry_event( + const struct sctp_association *asoc, gfp_t gfp) +{ + struct sctp_ulpevent *event; + struct sctp_sender_dry_event *sdry; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_sender_dry_event), + MSG_NOTIFICATION, gfp); + if (!event) + return NULL; + + skb = sctp_event2skb(event); + sdry = (struct sctp_sender_dry_event *) + skb_put(skb, sizeof(struct sctp_sender_dry_event)); + + sdry->sender_dry_type = SCTP_SENDER_DRY_EVENT; + sdry->sender_dry_flags = 0; + sdry->sender_dry_length = sizeof(struct sctp_sender_dry_event); + sctp_ulpevent_set_owner(event, asoc); + sdry->sender_dry_assoc_id = sctp_assoc2id(asoc); + + return event; +} /* Return the notification type, assuming this is a notification * event. |