diff options
| author | Sridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com> | 2002-10-29 19:27:39 -0800 |
|---|---|---|
| committer | Sridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com> | 2002-10-29 19:27:39 -0800 |
| commit | 39a990fea21586b031bedb29396b67eeaccd59f5 (patch) | |
| tree | 159d8418bab94439e421c96eb3bafce1d43fb28c | |
| parent | cca1b32f0390a06a3a489986da17a0eeb479599e (diff) | |
| parent | e8ece35bb74782acf839776b5c4315df81689a11 (diff) | |
Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/sctp-2.5
into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.44
| -rw-r--r-- | include/net/sctp/structs.h | 13 | ||||
| -rw-r--r-- | net/sctp/associola.c | 3 | ||||
| -rw-r--r-- | net/sctp/input.c | 5 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 69 | ||||
| -rw-r--r-- | net/sctp/output.c | 15 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 47 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 13 | ||||
| -rw-r--r-- | net/sctp/sm_statefuns.c | 5 | ||||
| -rw-r--r-- | net/sctp/transport.c | 74 |
9 files changed, 192 insertions, 52 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 5a8b75d2da26..9c3979f31d1f 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -246,7 +246,10 @@ typedef struct sctp_func { int optname, char *optval, int *optlen); - int (*get_dst_mtu) (const sockaddr_storage_t *address); + struct dst_entry *(*get_dst) (sockaddr_storage_t *daddr, + sockaddr_storage_t *saddr); + int (*cmp_saddr) (struct dst_entry *dst, + sockaddr_storage_t *saddr); __u16 net_header_len; int sockaddr_len; sa_family_t sa_family; @@ -476,6 +479,8 @@ struct SCTP_chunk { /* What is the origin IP address for this chunk? */ sockaddr_storage_t source; + /* Destination address for this chunk. */ + sockaddr_storage_t dest; /* For an inbound chunk, this tells us where it came from. * For an outbound chunk, it tells us where we'd like it to @@ -492,7 +497,7 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data); sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *, struct sock *); -void sctp_init_source(sctp_chunk_t *chunk); +void sctp_init_addrs(sctp_chunk_t *chunk); const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk); /* This is a structure for holding either an IPv6 or an IPv4 address. */ @@ -655,6 +660,9 @@ struct SCTP_transport { /* PMTU : The current known path MTU. */ __u32 pmtu; + /* Destination */ + struct dst_entry *dst; + /* When was the last time(in jiffies) that a data packet was sent on * this transport? This is used to adjust the cwnd when the transport * becomes inactive. @@ -735,6 +743,7 @@ extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int); extern sctp_transport_t *sctp_transport_init(sctp_transport_t *, const sockaddr_storage_t *, int); extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *); +extern void sctp_transport_route(sctp_transport_t *, sockaddr_storage_t *); extern void sctp_transport_free(sctp_transport_t *); extern void sctp_transport_destroy(sctp_transport_t *); extern void sctp_transport_reset_timers(sctp_transport_t *); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index c0217d260370..958ee7caa1cc 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -404,6 +404,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, sctp_transport_set_owner(peer, asoc); + /* Cache a route for the transport. */ + sctp_transport_route(peer, NULL); + /* If this is the first transport addr on this association, * initialize the association PMTU to the peer's PMTU. * If not and the current association PMTU is higher than the new diff --git a/net/sctp/input.c b/net/sctp/input.c index 8ed4aaaf085e..c2182d2d1b29 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -41,6 +41,7 @@ * Jon Grimm <jgrimm@us.ibm.com> * Hui Huang <hui.huang@nokia.com> * Daisy Chang <daisyc@us.ibm.com> + * Sridhar Samudrala <sri@us.ibm.com> * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. @@ -217,8 +218,8 @@ int sctp_rcv(struct sk_buff *skb) /* Remember the SCTP header. */ chunk->sctp_hdr = sh; - /* Set the source address. */ - sctp_init_source(chunk); + /* Set the source and destination addresses of the incoming chunk. */ + sctp_init_addrs(chunk); /* Remember where we came from. */ chunk->transport = transport; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 68f2c1b53519..5656f895e7bb 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -158,35 +158,61 @@ static inline int sctp_v6_xmit(struct sk_buff *skb) } #endif /* TEST_FRAME */ -/* Returns the mtu for the given v6 destination address. */ -int sctp_v6_get_dst_mtu(const sockaddr_storage_t *address) +/* FIXME: This macro needs to be moved to a common header file. */ +#define NIP6(addr) \ + ntohs((addr)->s6_addr16[0]), \ + ntohs((addr)->s6_addr16[1]), \ + ntohs((addr)->s6_addr16[2]), \ + ntohs((addr)->s6_addr16[3]), \ + ntohs((addr)->s6_addr16[4]), \ + ntohs((addr)->s6_addr16[5]), \ + ntohs((addr)->s6_addr16[6]), \ + ntohs((addr)->s6_addr16[7]) + +/* Returns the dst cache entry for the given source and destination ip + * addresses. + */ +struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr, + sockaddr_storage_t *saddr) { struct dst_entry *dst; - struct flowi fl; - int dst_mtu = SCTP_DEFAULT_MAXSEGMENT; + struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, + } } }; - fl.proto = 0; - fl.fl6_dst = (struct in6_addr *)&address->v6.sin6_addr; - fl.fl6_src = NULL; - fl.fl6_flowlabel = 0; - fl.oif = 0; - fl.uli_u.ports.sport = 0; - fl.uli_u.ports.dport = 0; + + SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", + __FUNCTION__, NIP6(fl.fl6_dst)); + + if (saddr) { + fl.fl6_src = &saddr->v6.sin6_addr; + SCTP_DEBUG_PRINTK( + "SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ", + NIP6(fl.fl6_src)); + } dst = ip6_route_output(NULL, &fl); if (dst) { - dst_mtu = dst->pmtu; - SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: " - "ip6_route_output: dev:%s pmtu:%d\n", - dst->dev->name, dst_mtu); - dst_release(dst); + struct rt6_info *rt; + rt = (struct rt6_info *)dst; + SCTP_DEBUG_PRINTK( + "rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " + "rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr)); + } else { - SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: " - "ip6_route_output failed, returning " - "%d as dst_mtu\n", dst_mtu); + SCTP_DEBUG_PRINTK("NO ROUTE\n"); + return NULL; } - return dst_mtu; + return dst; +} + +/* Check if the dst entry's source addr matches the given source addr. */ +int sctp_v6_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr) +{ + struct rt6_info *rt = (struct rt6_info *)dst; + + return ipv6_addr_cmp(&rt->rt6i_src.addr, &saddr->v6.sin6_addr); } /* Initialize a PF_INET6 socket msg_name. */ @@ -301,7 +327,8 @@ static sctp_func_t sctp_ipv6_specific = { .queue_xmit = sctp_v6_xmit, .setsockopt = ipv6_setsockopt, .getsockopt = ipv6_getsockopt, - .get_dst_mtu = sctp_v6_get_dst_mtu, + .get_dst = sctp_v6_get_dst, + .cmp_saddr = sctp_v6_cmp_saddr, .net_header_len = sizeof(struct ipv6hdr), .sockaddr_len = sizeof(struct sockaddr_in6), .sa_family = AF_INET6, diff --git a/net/sctp/output.c b/net/sctp/output.c index 966fcddfb974..5962d3e72e52 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -245,6 +245,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) int err = 0; int padding; /* How much padding do we need? */ __u8 packet_has_data = 0; + struct dst_entry *dst; /* Do NOT generate a chunkless packet... */ if (skb_queue_empty(&packet->chunks)) @@ -410,13 +411,6 @@ int sctp_packet_transmit(sctp_packet_t *packet) asoc->peer.last_sent_to = transport; } - /* Hey, before Linux changes, here's what we have to - * do to force IP routing to recognize the change of - * dest addr. --xguo - */ - if (sk->dst_cache) - sk->dst_cache->obsolete = 1; - if (packet_has_data) { struct timer_list *timer; unsigned long timeout; @@ -434,6 +428,13 @@ int sctp_packet_transmit(sctp_packet_t *packet) } } + dst = transport->dst; + if (!dst || dst->obsolete) { + sctp_transport_route(transport, NULL); + } + + nskb->dst = dst_clone(transport->dst); + SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n", nskb->len); (*transport->af_specific->queue_xmit)(nskb); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index db45c7afce18..5724996717d7 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -256,27 +256,41 @@ end_copy: return error; } -/* Returns the mtu for the given v4 destination address. */ -int sctp_v4_get_dst_mtu(const sockaddr_storage_t *address) +/* Returns the dst cache entry for the given source and destination ip + * addresses. + */ +struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr, + sockaddr_storage_t *saddr) { - int dst_mtu = SCTP_DEFAULT_MAXSEGMENT; struct rtable *rt; - struct flowi fl = { .nl_u = { .ip4_u = - { .daddr = address->v4.sin_addr.s_addr } } }; + struct flowi fl = { .nl_u = { .ip4_u = { .daddr = + daddr->v4.sin_addr.s_addr, + } } }; + + if (saddr) + fl.fl4_src = saddr->v4.sin_addr.s_addr; + + SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ", + __FUNCTION__, NIPQUAD(fl.fl4_dst), + NIPQUAD(fl.fl4_src)); if (ip_route_output_key(&rt, &fl)) { - SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu:ip_route_output_key" - " failed, returning %d as dst_mtu\n", - dst_mtu); - } else { - dst_mtu = rt->u.dst.pmtu; - SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu: " - "ip_route_output_key: dev:%s pmtu:%d\n", - rt->u.dst.dev->name, dst_mtu); - ip_rt_put(rt); + SCTP_DEBUG_PRINTK("NO ROUTE\n"); + return NULL; } - return dst_mtu; + SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n", + NIPQUAD(rt->rt_src), NIPQUAD(rt->rt_dst)); + + return &rt->u.dst; +} + +/* Check if the dst entry's source addr matches the given source addr. */ +int sctp_v4_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr) +{ + struct rtable *rt = (struct rtable *)dst; + + return (rt->rt_src == saddr->v4.sin_addr.s_addr); } /* Event handler for inet device events. @@ -438,7 +452,8 @@ sctp_func_t sctp_ipv4_specific = { .queue_xmit = ip_queue_xmit, .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, - .get_dst_mtu = sctp_v4_get_dst_mtu, + .get_dst = sctp_v4_get_dst, + .cmp_saddr = sctp_v4_cmp_saddr, .net_header_len = sizeof(struct iphdr), .sockaddr_len = sizeof(struct sockaddr_in), .sa_family = AF_INET, diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e1d6e9435210..fc0404a5880c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1030,16 +1030,17 @@ nodata: return retval; } -/* Set chunk->source based on the IP header in chunk->skb. */ -void sctp_init_source(sctp_chunk_t *chunk) +/* Set chunk->source and dest based on the IP header in chunk->skb. */ +void sctp_init_addrs(sctp_chunk_t *chunk) { - sockaddr_storage_t *source; + sockaddr_storage_t *source, *dest; struct sk_buff *skb; struct sctphdr *sh; struct iphdr *ih4; struct ipv6hdr *ih6; source = &chunk->source; + dest = &chunk->dest; skb = chunk->skb; ih4 = skb->nh.iph; ih6 = skb->nh.ipv6h; @@ -1050,6 +1051,9 @@ void sctp_init_source(sctp_chunk_t *chunk) source->v4.sin_family = AF_INET; source->v4.sin_port = ntohs(sh->source); source->v4.sin_addr.s_addr = ih4->saddr; + dest->v4.sin_family = AF_INET; + dest->v4.sin_port = ntohs(sh->dest); + dest->v4.sin_addr.s_addr = ih4->daddr; break; case 6: @@ -1057,6 +1061,9 @@ void sctp_init_source(sctp_chunk_t *chunk) source->v6.sin6_family = AF_INET6; source->v6.sin6_port = ntohs(sh->source); source->v6.sin6_addr = ih6->saddr; + dest->v6.sin6_family = AF_INET6; + dest->v6.sin6_port = ntohs(sh->dest); + dest->v6.sin6_addr = ih6->daddr; /* FIXME: What do we do with scope, etc. ? */ break; ) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index c6169254e99c..e0e39d0d9d1f 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4254,6 +4254,11 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, if (!packet) goto nomem_packet; + /* Cache a route for the transport with the chunk's destination as + * the source address. + */ + sctp_transport_route(transport, (sockaddr_storage_t *)&chunk->dest); + packet = sctp_packet_init(packet, transport, sport, dport); packet = sctp_packet_config(packet, vtag, 0, NULL); diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 316b9c3759fc..bfa6ba83ebe3 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -87,7 +87,6 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, peer->ipaddr = *addr; peer->af_specific = sctp_get_af_specific(addr); peer->asoc = NULL; - peer->pmtu = peer->af_specific->get_dst_mtu(addr); /* From 6.3.1 RTO Calculation: * @@ -161,6 +160,7 @@ void sctp_transport_destroy(sctp_transport_t *transport) if (transport->asoc) sctp_association_put(transport->asoc); + dst_release(transport->dst); kfree(transport); SCTP_DBG_OBJCNT_DEC(transport); } @@ -200,6 +200,78 @@ void sctp_transport_set_owner(sctp_transport_t *transport, sctp_association_hold(asoc); } +/* Caches the dst entry for a transport's destination address and an optional + * souce address. + */ +void sctp_transport_route(sctp_transport_t *transport, + sockaddr_storage_t *saddr) +{ + sctp_association_t *asoc = transport->asoc; + sctp_func_t *af = transport->af_specific; + sockaddr_storage_t *daddr = &transport->ipaddr; + sctp_bind_addr_t *bp; + rwlock_t *addr_lock; + struct sockaddr_storage_list *laddr; + struct list_head *pos; + struct dst_entry *dst; + + dst = af->get_dst(daddr, saddr); + + /* If there is no association or if a source address is passed, + * no more validation is required. + */ + if (!asoc || saddr) + goto out; + + if (SCTP_STATE_ESTABLISHED == asoc->state) { + bp = &asoc->base.bind_addr; + addr_lock = &asoc->base.addr_lock; + } else { + bp = &asoc->ep->base.bind_addr; + addr_lock = &asoc->ep->base.addr_lock; + } + + if (dst) { + /* Walk through the bind address list and look for a bind + * address that matches the source address of the returned dst. + */ + sctp_read_lock(addr_lock); + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, + list); + if (af->cmp_saddr(dst, &laddr->a)) + goto out_unlock; + } + sctp_read_unlock(addr_lock); + + /* None of the bound addresses match the source address of the + * dst. So release it. + */ + dst_release(dst); + } + + /* Walk through the bind address list and try to get a dst that + * matches a bind address as the source address. + */ + sctp_read_lock(addr_lock); + list_for_each(pos, &bp->address_list) { + laddr = list_entry(pos, struct sockaddr_storage_list, list); + + dst = af->get_dst(daddr, &laddr->a); + if (dst) + goto out_unlock; + } + +out_unlock: + sctp_read_unlock(addr_lock); +out: + transport->dst = dst; + if (dst) + transport->pmtu = dst->pmtu; + else + transport->pmtu = SCTP_DEFAULT_MAXSEGMENT; +} + /* Hold a reference to a transport. */ void sctp_transport_hold(sctp_transport_t *transport) { |
