summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-10-29 19:27:39 -0800
committerSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-10-29 19:27:39 -0800
commit39a990fea21586b031bedb29396b67eeaccd59f5 (patch)
tree159d8418bab94439e421c96eb3bafce1d43fb28c
parentcca1b32f0390a06a3a489986da17a0eeb479599e (diff)
parente8ece35bb74782acf839776b5c4315df81689a11 (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.h13
-rw-r--r--net/sctp/associola.c3
-rw-r--r--net/sctp/input.c5
-rw-r--r--net/sctp/ipv6.c69
-rw-r--r--net/sctp/output.c15
-rw-r--r--net/sctp/protocol.c47
-rw-r--r--net/sctp/sm_make_chunk.c13
-rw-r--r--net/sctp/sm_statefuns.c5
-rw-r--r--net/sctp/transport.c74
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)
{