* Re: [PATCH net-next-2.6 v6 2/5] sctp: Add sysctl support for Auto-ASCONF
From: Wei Yongjun @ 2011-04-26 5:30 UTC (permalink / raw)
To: Michio Honda; +Cc: netdev, lksctp-developers
In-Reply-To: <4B304B0D-35AC-4372-84F3-EFBC5A4C7BF2@sfc.wide.ad.jp>
> This patch allows the system administrator to change default Auto-ASCONF on/off behavior via an sysctl value.
>
> Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
>
Acked-by: Wei Yongjun <yjwei@cn.fujitsu.com>
^ permalink raw reply
* Re: [PATCH net-next-2.6 v6 1/5] sctp: Add Auto-ASCONF support
From: Wei Yongjun @ 2011-04-26 5:30 UTC (permalink / raw)
To: Michio Honda; +Cc: netdev, lksctp-developers
In-Reply-To: <5D4D424A-5545-44C4-909A-4DDE594BD5D4@sfc.wide.ad.jp>
> SCTP reconfigure the IP addresses in the association by using ASCONF chunks as mentioned in RFC5061.
> For example, we can start to use the newly configured IP address in the existing association.
> ASCONF operation is invoked in two ways:
> First is done by the application to call sctp_bindx() system call.
> Second is automatic operation in the SCTP stack with address events in the host computer (called auto_asconf) .
> The former is already implemented, and this patch implement the latter.
>
> Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
>
Acked-by: Wei Yongjun <yjwei@cn.fujitsu.com>
^ permalink raw reply
* Re: [PATCH] netfilter/IPv6: initialize TOS field in REJECT target module
From: Fernando Luis Vazquez Cao @ 2011-04-26 5:25 UTC (permalink / raw)
To: David Miller
Cc: pablo, eric.dumazet, netfilter-devel, netdev, yoshfuji, jengelh
In-Reply-To: <20110425.221744.226775617.davem@davemloft.net>
On Mon, 2011-04-25 at 22:17 -0700, David Miller wrote:
> From: Fernando Luis Vazquez Cao <fernando@oss.ntt.co.jp>
> Date: Tue, 26 Apr 2011 10:26:20 +0900
>
> > On Tue, 2011-04-26 at 03:13 +0200, Pablo Neira Ayuso wrote:
> >> On 22/04/11 10:37, Eric Dumazet wrote:
> >> > Le vendredi 22 avril 2011 à 17:11 +0900, Fernando Luis Vazquez Cao a
> >> > écrit :
> >> >
> >> >> Thank you!
> >> >>
> >> >> Should we send these two patches to -stable too?
> >> >
> >> > David takes care of stable submissions for netdev stuff, thanks.
> >>
> >> If the patch follows the netfilter path, we'll take care of sending
> >> stable submissions.
> >
> > David, will you take care of these two patches or should they go through
> > the netfilter tree?
>
> Netfilter, as usual.
Thank you for the clarification. I really appreciate it.
Pablo, could you pull in the two patches below? They have already been
acked by Eric. It would be great it we could get them merged for the
next -rc and stable releases.
[PATCH] netfilter/IPv6: fix DSCP mangle code
[PATCH] netfilter/IPv6: initialize TOS field in REJECT target module
- Fernando
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] netfilter/IPv6: initialize TOS field in REJECT target module
From: David Miller @ 2011-04-26 5:17 UTC (permalink / raw)
To: fernando; +Cc: pablo, eric.dumazet, netfilter-devel, netdev, yoshfuji, jengelh
In-Reply-To: <1303781180.2874.13.camel@nausicaa>
From: Fernando Luis Vazquez Cao <fernando@oss.ntt.co.jp>
Date: Tue, 26 Apr 2011 10:26:20 +0900
> On Tue, 2011-04-26 at 03:13 +0200, Pablo Neira Ayuso wrote:
>> On 22/04/11 10:37, Eric Dumazet wrote:
>> > Le vendredi 22 avril 2011 à 17:11 +0900, Fernando Luis Vazquez Cao a
>> > écrit :
>> >
>> >> Thank you!
>> >>
>> >> Should we send these two patches to -stable too?
>> >
>> > David takes care of stable submissions for netdev stuff, thanks.
>>
>> If the patch follows the netfilter path, we'll take care of sending
>> stable submissions.
>
> David, will you take care of these two patches or should they go through
> the netfilter tree?
Netfilter, as usual.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] netfilter/IPv6: initialize TOS field in REJECT target module
From: David Miller @ 2011-04-26 5:17 UTC (permalink / raw)
To: pablo; +Cc: eric.dumazet, fernando, netfilter-devel, netdev, yoshfuji,
jengelh
In-Reply-To: <4DB61C2C.7060508@netfilter.org>
From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: Tue, 26 Apr 2011 03:13:16 +0200
> On 22/04/11 10:37, Eric Dumazet wrote:
>> Le vendredi 22 avril 2011 à 17:11 +0900, Fernando Luis Vazquez Cao a
>> écrit :
>>
>>> Thank you!
>>>
>>> Should we send these two patches to -stable too?
>>
>> David takes care of stable submissions for netdev stuff, thanks.
>
> If the patch follows the netfilter path, we'll take care of sending
> stable submissions.
Right.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: how to set vlan filter for intel 82599
From: zhou rui @ 2011-04-26 4:39 UTC (permalink / raw)
To: Ben Hutchings; +Cc: netdev
In-Reply-To: <1303789868.3032.347.camel@localhost>
On Tue, Apr 26, 2011 at 11:51 AM, Ben Hutchings
<bhutchings@solarflare.com> wrote:
> On Tue, 2011-04-26 at 11:39 +0800, zhou rui wrote:
>> On Tue, Apr 26, 2011 at 10:57 AM, Ben Hutchings
>> <bhutchings@solarflare.com> wrote:
>> > On Tue, 2011-04-26 at 10:19 +0800, zhou rui wrote:
>> >> hi
>> >> here is the problem troubles me,how to set vlan filter for intel
>> >> 82599? for example
>> >> I want vlan id 0~31 will go to queue 0, vlan id 32-63 will go to queue
>> >> 1...below is my setting,but doesn't work
>> >>
>> >> don't know the exact meanning of the vlan-mask and vlan,how are they calculated?
>> >>
>> >> ./ethtool -K eth5 ntuple on
>> >>
>> >> ./ethtool -U eth5 flow-type udp4 src-ip 0x0 src-ip-mask 0x0 dst-ip 0x0
>> >> dst-ip-mask 0x0 src-port 0x0 src-port-mask 0x0 dst-port 0x0
>> >> dst-port-mask 0x0 vlan 0x0000 vlan-mask 0x00E0 user-def 0x0
>> >> user-def-mask 0x0 action 0
>> > [...]
>> >
>> > This specifies a filter for UDP/IPv4 packets, and the masks are wrong.
>> > If you actually wanted to filter only UDP/IPv4 packets for VID 0-31 then
>> > the correct syntax would be:
>> >
>> > ethtool -U eth5 flow-type udp4 vlan 0 vlan-mask 0xf01f
>> >
>> > If you don't care about the layer 3/4 protocols then you would need to
>> > use 'flow-type ether', but no driver implements that yet. (Well, sfc
>> > implements the *type*, but not filtering by VID only.)
> [...]
>> hi ben,thanks for your help,would you mind tell me "32~63" VID filter?
>> still can not understand the vlan-mask
>
> The vlan-mask specifies tag bits to be ignored. You want to ignore the
> priority, CFI and lower 5 bits of the VID, hence 0xf01f. For a
> different group of 32 VIDs you would only change the vlan argument, not
> the vlan-mask argument.
>
> Ben.
>
> --
> Ben Hutchings, Senior Software Engineer, Solarflare
> Not speaking for my employer; that's the marketing department's job.
> They asked us to note that Solarflare product names are trademarked.
>
>
i set the filter like below:
for a vlanid=50, it always match the last rule (action 7)
./ethtool -K eth5 ntuple off
./ethtool -K eth5 ntuple on
./ethtool -U eth5 flow-type tcp4 vlan 32 vlan-mask 0xF01F action 1
./ethtool -U eth5 flow-type udp4 vlan 32 vlan-mask 0xF01F action 1
./ethtool -U eth5 flow-type udp4 vlan 64 vlan-mask 0xF01F action 7
./ethtool -U eth5 flow-type tcp4 vlan 64 vlan-mask 0xF01F action 7
I tried the latest ixgbe driver 3.3.9, it reports:
Cannot add new RX n-tuple filter: Operation not permitted
./ethtool -V
ethtool version 2.6.36
thanks!
^ permalink raw reply
* [PATCH 3/3] forcedeth: Support for byte queue limits
From: Tom Herbert @ 2011-04-26 4:38 UTC (permalink / raw)
To: davem, netdev
Changes to forcedeth to use byte queue limits.
Signed-off-by: Tom Herbert <therbert@google.com>
---
drivers/net/forcedeth.c | 38 ++++++++++++++++++++++++++++++++++----
1 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 0e1c76a..00f9f99 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -1827,6 +1827,11 @@ static void nv_init_rx(struct net_device *dev)
}
}
+static struct netdev_queue *nv_netdev_queue(struct fe_priv *np)
+{
+ return netdev_get_tx_queue(np->dev, 0);
+}
+
static void nv_init_tx(struct net_device *dev)
{
struct fe_priv *np = netdev_priv(dev);
@@ -1843,6 +1848,7 @@ static void nv_init_tx(struct net_device *dev)
np->tx_pkts_in_progress = 0;
np->tx_change_owner = NULL;
np->tx_end_flip = NULL;
+ netdev_queue_bql_reset(nv_netdev_queue(np));
np->tx_stop = 0;
for (i = 0; i < np->tx_ring_size; i++) {
@@ -2107,7 +2113,8 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&np->lock, flags);
empty_slots = nv_get_empty_tx_slots(np);
- if (unlikely(empty_slots <= entries)) {
+ if (unlikely(empty_slots <= entries ||
+ !netdev_queue_bytes_avail(nv_netdev_queue(np)))) {
netif_stop_queue(dev);
np->tx_stop = 1;
spin_unlock_irqrestore(&np->lock, flags);
@@ -2180,6 +2187,9 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* set tx flags */
start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra);
+
+ netdev_queue_bytes_sent(nv_netdev_queue(np), skb->len);
+
np->put_tx.orig = put_tx;
spin_unlock_irqrestore(&np->lock, flags);
@@ -2216,7 +2226,8 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
spin_lock_irqsave(&np->lock, flags);
empty_slots = nv_get_empty_tx_slots(np);
- if (unlikely(empty_slots <= entries)) {
+ if (unlikely(empty_slots <= entries ||
+ !netdev_queue_bytes_avail(nv_netdev_queue(np)))) {
netif_stop_queue(dev);
np->tx_stop = 1;
spin_unlock_irqrestore(&np->lock, flags);
@@ -2319,6 +2330,9 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
/* set tx flags */
start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra);
+
+ netdev_queue_bytes_sent(nv_netdev_queue(np), skb->len);
+
np->put_tx.ex = put_tx;
spin_unlock_irqrestore(&np->lock, flags);
@@ -2356,6 +2370,7 @@ static int nv_tx_done(struct net_device *dev, int limit)
u32 flags;
int tx_work = 0;
struct ring_desc *orig_get_tx = np->get_tx.orig;
+ unsigned long bytes_cleaned = 0;
while ((np->get_tx.orig != np->put_tx.orig) &&
!((flags = le32_to_cpu(np->get_tx.orig->flaglen)) & NV_TX_VALID) &&
@@ -2395,6 +2410,7 @@ static int nv_tx_done(struct net_device *dev, int limit)
dev->stats.tx_packets++;
dev->stats.tx_bytes += np->get_tx_ctx->skb->len;
}
+ bytes_cleaned += np->get_tx_ctx->skb->len;
dev_kfree_skb_any(np->get_tx_ctx->skb);
np->get_tx_ctx->skb = NULL;
tx_work++;
@@ -2405,7 +2421,12 @@ static int nv_tx_done(struct net_device *dev, int limit)
if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
np->get_tx_ctx = np->first_tx_ctx;
}
- if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx))) {
+
+ if (bytes_cleaned)
+ netdev_queue_bytes_completed(nv_netdev_queue(np),
+ bytes_cleaned);
+ if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx) &&
+ netdev_queue_bytes_avail(nv_netdev_queue(np)))) {
np->tx_stop = 0;
netif_wake_queue(dev);
}
@@ -2418,6 +2439,7 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit)
u32 flags;
int tx_work = 0;
struct ring_desc_ex *orig_get_tx = np->get_tx.ex;
+ unsigned long bytes_cleaned = 0;
while ((np->get_tx.ex != np->put_tx.ex) &&
!((flags = le32_to_cpu(np->get_tx.ex->flaglen)) & NV_TX2_VALID) &&
@@ -2437,6 +2459,7 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit)
}
}
+ bytes_cleaned += np->get_tx_ctx->skb->len;
dev_kfree_skb_any(np->get_tx_ctx->skb);
np->get_tx_ctx->skb = NULL;
tx_work++;
@@ -2449,7 +2472,12 @@ static int nv_tx_done_optimized(struct net_device *dev, int limit)
if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
np->get_tx_ctx = np->first_tx_ctx;
}
- if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx))) {
+
+ if (bytes_cleaned)
+ netdev_queue_bytes_completed(nv_netdev_queue(np),
+ bytes_cleaned);
+ if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx) &&
+ netdev_queue_bytes_avail(nv_netdev_queue(np)))) {
np->tx_stop = 0;
netif_wake_queue(dev);
}
@@ -5263,6 +5291,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
np->stats_poll.data = (unsigned long) dev;
np->stats_poll.function = nv_do_stats_poll; /* timer handler */
+ netdev_queue_bql_init(nv_netdev_queue(np));
+
err = pci_enable_device(pci_dev);
if (err)
goto out_free;
--
1.7.3.1
^ permalink raw reply related
* [PATCH 2/3] bql: Byte queue limits
From: Tom Herbert @ 2011-04-26 4:38 UTC (permalink / raw)
To: davem, netdev
Networking stack support for byte queue limits, uses dynamic queue
limits library. Byte queue limits are maintained per transmit queue,
and a bql structure has been added to netdev_queue structure for this
purpose.
Configuration of bql is in the tx-<n> sysfs directory for the queue
under the byte_queue_limits directory. Configuration includes:
limit_min, bql minimum limit
limit_max, bql maximum limit
hold_time, bql slack hold time
Also under the directory are:
limit, current byte limit
inflight, current number of bytes on the queue
Signed-off-by: Tom Herbert <therbert@google.com>
---
include/linux/netdevice.h | 46 +++++++++++++++-
net/core/net-sysfs.c | 137 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 177 insertions(+), 6 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index cb8178a..0a76b88 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -44,6 +44,7 @@
#include <linux/rculist.h>
#include <linux/dmaengine.h>
#include <linux/workqueue.h>
+#include <linux/dynamic_queue_limits.h>
#include <linux/ethtool.h>
#include <net/net_namespace.h>
@@ -556,8 +557,10 @@ struct netdev_queue {
struct Qdisc *qdisc;
unsigned long state;
struct Qdisc *qdisc_sleeping;
-#ifdef CONFIG_RPS
+#ifdef CONFIG_XPS
struct kobject kobj;
+ bool do_bql;
+ struct dql dql;
#endif
#if defined(CONFIG_XPS) && defined(CONFIG_NUMA)
int numa_node;
@@ -589,6 +592,47 @@ static inline void netdev_queue_numa_node_write(struct netdev_queue *q, int node
#endif
}
+/*
+ * Definitions for byte queue limits for TX queue.
+ */
+static inline void netdev_queue_bql_init(struct netdev_queue *q)
+{
+#ifdef CONFIG_XPS
+ dql_init(&q->dql, 1000);
+ q->do_bql = true;
+#endif
+}
+
+static inline void netdev_queue_bql_reset(struct netdev_queue *q)
+{
+#ifdef CONFIG_XPS
+ dql_reset(&q->dql);
+#endif
+}
+
+static inline bool netdev_queue_bytes_avail(struct netdev_queue *q)
+{
+#ifdef CONFIG_XPS
+ return dql_avail(&q->dql) >= 0;
+#endif
+}
+
+static inline void netdev_queue_bytes_sent(struct netdev_queue *q,
+ unsigned count)
+{
+#ifdef CONFIG_XPS
+ dql_queued(&q->dql, count);
+#endif
+}
+
+static inline void netdev_queue_bytes_completed(struct netdev_queue *q,
+ unsigned count)
+{
+#ifdef CONFIG_XPS
+ dql_completed(&q->dql, count);
+#endif
+}
+
#ifdef CONFIG_RPS
/*
* This structure holds an RPS map which can be of variable length. The
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 5ceb257..5f29b8e 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -20,6 +20,7 @@
#include <linux/rtnetlink.h>
#include <linux/wireless.h>
#include <linux/vmalloc.h>
+#include <linux/jiffies.h>
#include <net/wext.h>
#include "net-sysfs.h"
@@ -852,6 +853,119 @@ static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue)
return i;
}
+static ssize_t bql_show(char *buf, unsigned long value)
+{
+ int p = 0;
+
+ p = sprintf(buf, "%lu\n", value);
+ return p;
+}
+
+static ssize_t bql_set(const char *buf, const size_t count,
+ unsigned long *pvalue)
+{
+ unsigned long value;
+ int err;
+
+ if (!strcmp(buf, "max") || !strcmp(buf, "max\n"))
+ value = DQL_MAX_LIMIT;
+ else {
+ err = kstrtoul(buf, 10, &value);
+ if (err < 0)
+ return err;
+ if (value > DQL_MAX_LIMIT)
+ return -EINVAL;
+ }
+
+ *pvalue = value;
+
+ return count;
+}
+
+static ssize_t bql_show_hold_time(struct netdev_queue *queue,
+ struct netdev_queue_attribute *attr,
+ char *buf)
+{
+ struct dql *dql = &queue->dql;
+ int p = 0;
+
+ p = sprintf(buf, "%u\n", jiffies_to_msecs(dql->slack_hold_time));
+
+ return p;
+}
+
+static ssize_t bql_set_hold_time(struct netdev_queue *queue,
+ struct netdev_queue_attribute *attribute,
+ const char *buf, size_t len)
+{
+ struct dql *dql = &queue->dql;
+ unsigned value;
+ int err;
+
+ err = kstrtouint(buf, 10, &value);
+ if (err < 0)
+ return err;
+
+ dql->slack_hold_time = msecs_to_jiffies(value);
+
+ return len;
+}
+
+static struct netdev_queue_attribute bql_hold_time_attribute =
+ __ATTR(hold_time, S_IRUGO | S_IWUSR, bql_show_hold_time,
+ bql_set_hold_time);
+
+static ssize_t bql_show_inflight(struct netdev_queue *queue,
+ struct netdev_queue_attribute *attr,
+ char *buf)
+{
+ struct dql *dql = &queue->dql;
+ int p = 0;
+
+ p = sprintf(buf, "%lu\n", dql->num_queued - dql->num_completed);
+
+ return p;
+}
+
+static struct netdev_queue_attribute bql_inflight_attribute =
+ __ATTR(inflight, S_IRUGO | S_IWUSR, bql_show_inflight, NULL);
+
+#define BQL_ATTR(NAME, FIELD) \
+static ssize_t bql_show_ ## NAME(struct netdev_queue *queue, \
+ struct netdev_queue_attribute *attr, \
+ char *buf) \
+{ \
+ return bql_show(buf, queue->dql.FIELD); \
+} \
+ \
+static ssize_t bql_set_ ## NAME(struct netdev_queue *queue, \
+ struct netdev_queue_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return bql_set(buf, len, &queue->dql.FIELD); \
+} \
+ \
+static struct netdev_queue_attribute bql_ ## NAME ## _attribute = \
+ __ATTR(NAME, S_IRUGO | S_IWUSR, bql_show_ ## NAME, \
+ bql_set_ ## NAME);
+
+BQL_ATTR(limit, limit)
+BQL_ATTR(limit_max, max_limit)
+BQL_ATTR(limit_min, min_limit)
+
+static struct attribute *dql_attrs[] = {
+ &bql_limit_attribute.attr,
+ &bql_limit_max_attribute.attr,
+ &bql_limit_min_attribute.attr,
+ &bql_hold_time_attribute.attr,
+ &bql_inflight_attribute.attr,
+ NULL
+};
+
+static struct attribute_group dql_group = {
+ .name = "byte_queue_limits",
+ .attrs = dql_attrs,
+};
static ssize_t show_xps_map(struct netdev_queue *queue,
struct netdev_queue_attribute *attribute, char *buf)
@@ -1119,14 +1233,22 @@ static int netdev_queue_add_kobject(struct net_device *net, int index)
kobj->kset = net->queues_kset;
error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL,
"tx-%u", index);
- if (error) {
- kobject_put(kobj);
- return error;
+ if (error)
+ goto exit;
+
+ if (queue->do_bql) {
+ error = sysfs_create_group(kobj, &dql_group);
+ if (error)
+ goto kset_exit;
}
kobject_uevent(kobj, KOBJ_ADD);
dev_hold(queue->dev);
+ return 0;
+kset_exit:
+ kobject_put(kobj);
+exit:
return error;
}
#endif /* CONFIG_XPS */
@@ -1146,8 +1268,13 @@ netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num)
}
}
- while (--i >= new_num)
- kobject_put(&net->_tx[i].kobj);
+ while (--i >= new_num) {
+ struct netdev_queue *queue = net->_tx + i;
+
+ if (queue->do_bql)
+ sysfs_remove_group(&queue->kobj, &dql_group);
+ kobject_put(&queue->kobj);
+ }
return error;
#else
--
1.7.3.1
^ permalink raw reply related
* [PATCH 1/3] dql: Dynamic queue limits
From: Tom Herbert @ 2011-04-26 4:38 UTC (permalink / raw)
To: davem, netdev
Implementation of dynamic queue limits (dql). This is a libary which
allows a queue limit to be dynamically managed. The goal of dql is
to set the queue limit, number of ojects to the queue, to be minimized
without allowing the queue to be starved.
dql would be used with a queue whose use has these properties:
1) Objects are queued up to some limit which can be expressed as a
count of objects.
2) Periodically a completion process executes which retires consumed
objects.
3) Starvation occurs when limit has been reached, all queued data has
actually been consumed but completion processing has not yet run,
so queuing new data is blocked.
4) Minimizing the amount of queued data is desirable.
A canonical example of such a queue would be a NIC HW transmit queue.
The queue limit is dynamic, it will increase or decrease over time
depending on the workload. The queue limit is recalculated each time
completion processing is done. Increases occur when the queue is
starved and can exponentially increase over successive intervals.
Decreases occur when more data is being maintained in the queue than
needed to prevent starvation. The number of extra objects, or "slack",
is measured over successive intervals, and to avoid hysteresis the
limit is only reduced by the miminum slack seen over a configurable
time period.
dql API provides routines to manage the queue:
- dql_init is called to intialize the dql structure
- dql_reset is called to reset dynamic structures
- dql_queued when objects are being enqueued
- dql_avail returns availability in the queue
- dql_completed is called when objects have be consumed in the queue
Configuration consists of:
- max_limit, maximum limit
- min_limt, minimum limit
- slack_hold_time, time to measure instances of slack before reducing
queue limit.
Signed-off-by: Tom Herbert <therbert@google.com>
---
include/linux/dynamic_queue_limits.h | 80 ++++++++++++++++++++
lib/Makefile | 3 +-
lib/dynamic_queue_limits.c | 132 ++++++++++++++++++++++++++++++++++
3 files changed, 214 insertions(+), 1 deletions(-)
create mode 100644 include/linux/dynamic_queue_limits.h
create mode 100644 lib/dynamic_queue_limits.c
diff --git a/include/linux/dynamic_queue_limits.h b/include/linux/dynamic_queue_limits.h
new file mode 100644
index 0000000..3ffc591
--- /dev/null
+++ b/include/linux/dynamic_queue_limits.h
@@ -0,0 +1,80 @@
+/*
+ * Dynamic queue limits (dql) - Definitions
+ *
+ * Author: Tom Herbert (therbert@google.com)
+ *
+ * This header file contains the definitions for dynamic queue limits (dql).
+ * dql would be used in conjunction with a producer/consumer type queue
+ * (possibly a HW queue). Such a queue would have these general properties:
+ *
+ * 1) Objects are queued up to some limit.
+ * 2) Periodically a completion process executes which retires consumed
+ * objects.
+ * 3) Starvation occurs when limit has been reached, all queued data has
+ * actually been consumed but completion processing has not yet run
+ * so queuing new data is blocked.
+ * 4) Minimizing the amount of queued data is desirable.
+ *
+ * The goal of dql is to calculate the limit as the minimum number of objects
+ * needed to prevent starvation.
+ *
+ * The dql implemenation does not implement any locking for the dql data
+ * structures, the higher layer should provide this.
+ */
+
+#ifndef _LINUX_DQL_H
+#define _LINUX_DQL_H
+
+#ifdef __KERNEL__
+
+struct dql {
+ unsigned long limit; /* Current limit */
+ unsigned long prev_ovlimit; /* Previous over limit */
+
+ unsigned long num_queued; /* Total ever queued */
+ unsigned long prev_num_queued; /* Previous queue total */
+ unsigned long num_completed; /* Total ever completed */
+
+ unsigned long last_obj_cnt; /* Count at last queuing */
+ unsigned long prev_last_obj_cnt; /* Previous queuing cnt */
+
+ unsigned long lowest_slack; /* Lowest slack found */
+ unsigned long slack_start_time; /* Time slacks seen */
+
+ unsigned long max_limit; /* Maximum limit */
+ unsigned long min_limit; /* Minimum limit */
+ unsigned slack_hold_time; /* Time to measure slack */
+};
+
+/* Set some static maximums */
+#define DQL_MAX_OBJECT (-1UL / 16)
+#define DQL_MAX_LIMIT ((-1UL / 2) - DQL_MAX_OBJECT)
+
+/* Record number of objects queued. */
+static inline void dql_queued(struct dql *dql, unsigned long count)
+{
+ BUG_ON(count > DQL_MAX_OBJECT);
+ BUG_ON(dql->num_queued - dql->num_completed > DQL_MAX_LIMIT);
+
+ dql->num_queued += count;
+ dql->last_obj_cnt = count;
+}
+
+/* Returns how many objects can be queued, < 0 indicates over limit. */
+static inline long dql_avail(struct dql *dql)
+{
+ return dql->limit - (dql->num_queued - dql->num_completed);
+}
+
+/* Record number of completed objects and recalculate the limit. */
+extern void dql_completed(struct dql *dql, unsigned long count);
+
+/* Reset dql state */
+extern void dql_reset(struct dql *dql);
+
+/* Initialize dql state */
+extern int dql_init(struct dql *dql, unsigned hold_time);
+
+#endif /* _KERNEL_ */
+
+#endif /* _LINUX_DQL_H */
diff --git a/lib/Makefile b/lib/Makefile
index ef0f285..47b3605 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -21,7 +21,8 @@ lib-y += kobject.o kref.o klist.o
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
- string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o
+ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \
+ dynamic_queue_limits.o
obj-y += kstrtox.o
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
diff --git a/lib/dynamic_queue_limits.c b/lib/dynamic_queue_limits.c
new file mode 100644
index 0000000..6a1f5b9
--- /dev/null
+++ b/lib/dynamic_queue_limits.c
@@ -0,0 +1,132 @@
+/*
+ * Dynamic byte queue limits. See include/linux/dynamic_queue_limits.h
+ *
+ * Author: Tom Herbert (therbert@google.com)
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/dynamic_queue_limits.h>
+
+#define POSDIFF(A, B) ((A) > (B) ? (A) - (B) : 0)
+
+/* Records completed count and recalculates the queue limit */
+void dql_completed(struct dql *dql, unsigned long count)
+{
+ unsigned long inprogress, prev_inprogress, limit;
+ unsigned long ovlimit, all_prev_completed, completed;
+
+ /* Can't complete more than what's in queue */
+ BUG_ON(count > dql->num_queued - dql->num_completed);
+
+ completed = dql->num_completed + count;
+ limit = dql->limit;
+ ovlimit = POSDIFF(dql->num_queued - dql->num_completed, limit);
+ inprogress = dql->num_queued - completed;
+ prev_inprogress = dql->prev_num_queued - dql->num_completed;
+ all_prev_completed = POSDIFF(completed, dql->prev_num_queued);
+
+ if ((ovlimit && !inprogress) ||
+ (dql->prev_ovlimit && all_prev_completed)) {
+ /*
+ * Queue considered starved if:
+ * - The queue was over-limit in the last interval,
+ * and there is no more data in the queue.
+ * OR
+ * - The queue was over-limit in the previous interval and
+ * when enqueuing it was possible that all queued data
+ * had been consumed. This covers the case when queue
+ * may have becomes starved between completion processing
+ * running and next time enqueue was scheduled.
+ *
+ * When queue is starved increase the limit by the amount
+ * of bytes both sent and completed in the last interval,
+ * plus any previous over-limit.
+ */
+ limit += POSDIFF(completed, dql->prev_num_queued) +
+ dql->prev_ovlimit;
+ dql->slack_start_time = jiffies;
+ dql->lowest_slack = -1UL;
+ } else if (inprogress && prev_inprogress && !all_prev_completed) {
+ /*
+ * Queue was not starved, check if the limit can be decreased.
+ * A decrease is only considered if the queue has been busy in
+ * the whole interval (the check above).
+ *
+ * If there is slack, the amount execess data queued above the
+ * the amount needed to prevent starvation, the queue limit can
+ * be decreased. To avoid hysteresis we consider the
+ * minimum amount of slack found over several iterations of the
+ * completion routine.
+ */
+ unsigned long slack, slack_last_objs;
+
+ /*
+ * Slack is the maximum of
+ * - The queue limit plus previous over-limit minus twice
+ * the number of objects completed. Note that two times
+ * number of completed bytes is basis for upper bound
+ * of the limit.
+ * - Portion of objects in the last queuing operation that
+ * was not part of non-zero previous over-limit. That is
+ * "round down" by non-overlimit portion of the last
+ * queueing operation.
+ */
+ slack = POSDIFF(limit + dql->prev_ovlimit,
+ 2 * (completed - dql->num_completed));
+ slack_last_objs = dql->prev_ovlimit ?
+ POSDIFF(dql->prev_last_obj_cnt, dql->prev_ovlimit) : 0;
+
+ slack = max(slack, slack_last_objs);
+
+ if (slack < dql->lowest_slack)
+ dql->lowest_slack = slack;
+
+ if (time_after(jiffies,
+ dql->slack_start_time + dql->slack_hold_time)) {
+ limit = POSDIFF(limit, dql->lowest_slack);
+ dql->slack_start_time = jiffies;
+ dql->lowest_slack = -1UL;
+ }
+ }
+
+ /* Enforce bounds on limit */
+ limit = clamp(limit, dql->min_limit, dql->max_limit);
+
+ if (limit != dql->limit) {
+ dql->limit = limit;
+ ovlimit = 0;
+ }
+
+ dql->prev_ovlimit = ovlimit;
+ dql->prev_last_obj_cnt = dql->last_obj_cnt;
+ dql->num_completed = completed;
+ dql->prev_num_queued = dql->num_queued;
+}
+EXPORT_SYMBOL(dql_completed);
+
+void dql_reset(struct dql *dql)
+{
+ /* Reset all dynamic values */
+ dql->limit = 0;
+ dql->num_queued = 0;
+ dql->num_completed = 0;
+ dql->last_obj_cnt = 0;
+ dql->prev_num_queued = 0;
+ dql->prev_last_obj_cnt = 0;
+ dql->prev_ovlimit = 0;
+ dql->lowest_slack = -1UL;
+ dql->slack_start_time = jiffies;
+}
+EXPORT_SYMBOL(dql_reset);
+
+int dql_init(struct dql *dql, unsigned hold_time)
+{
+ dql->max_limit = DQL_MAX_LIMIT;
+ dql->min_limit = 0;
+ dql->slack_hold_time = hold_time;
+ dql_reset(dql);
+ return 0;
+}
+EXPORT_SYMBOL(dql_init);
--
1.7.3.1
^ permalink raw reply related
* [PATCH 0/3] net: Byte queue limit patch series
From: Tom Herbert @ 2011-04-26 4:38 UTC (permalink / raw)
To: davem, netdev
This patch series implements byte queue limits (bql) for NIC TX queues.
Byte queue limits are a mechanism to limit the size of the transmit
hardware queue on a NIC by number of bytes. The goal of these byte
limits is too reduce latency caused by excessive queuing in hardware
without sacrificing throughput.
Hardware queuing limits are typically specified in terms of a number
hardware descriptors, each of which has a variable size. The variability
of the size of individual queued items can have a very wide range. For
instance with the e1000 NIC the size could range from 64 bytes to 4K
(with TSO enabled). This variability makes it next to impossible to
choose a single queue limit that prevents starvation and provides lowest
possible latency.
The objective of byte queue limits is to set the limit to be the
minimum needed to prevent starvation between successive transmissions to
the hardware. The latency between two transmissions can be variable in a
system. It is dependent on interrupt frequency, NAPI polling latencies,
scheduling of the queuing discipline, lock contention, etc. Therefore we
propose that byte queue limits should be dynamic and change in
iaccordance with networking stack latencies a system encounters.
Patches to implement this:
Patch 1: Dynamic queue limits (dql) library. This provides the general
queuing algorithm.
Patch 2: netdev changes that use dlq to support byte queue limits.
Patch 3: Support in forcedeth drvier for byte queue limits.
The effects of BQL are demonstrated in the benchmark results below.
These were made running 200 stream of netperf RR tests:
140000 rr size
BQL: 80-215K bytes in queue, 856 tps, 3.26%
No BQL: 2700-2930K bytes in queue, 854 tps, 3.71% cpu
14000 rr size
BQ: 25-55K bytes in queue, 8500 tps
No BQL: 1500-1622K bytes in queue, 8523 tps, 4.53% cpu
1400 rr size
BQL: 20-38K in queue bytes in queue, 86582 tps, 7.38% cpu
No BQL: 29-117K 85738 tps, 7.67% cpu
140 rr size
BQL: 1-10K bytes in queue, 320540 tps, 34.6% cpu
No BQL: 1-13K bytes in queue, 323158, 37.16% cpu
1 rr size
BQL: 0-3K in queue, 338811 tps, 41.41% cpu
No BQL: 0-3K in queue, 339947 42.36% cpu
The amount of queuing in the NIC is reduced up to 90%, and I haven't
yet seen a consistent negative impact in terms of throughout or
CPU utilization.
^ permalink raw reply
* [PATCH net-next-2.6 v6 5/5] sctp: Add ASCONF operation on the single-homed host
From: Michio Honda @ 2011-04-26 4:30 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
SCTP can change the IP address on the single-homed host.
In this case, the SCTP association transmits an ASCONF packet including addition of the new IP address and deletion of the old address. This patch implements this functionality.
In this case, the ASCONF chunk is added to the beginning of the queue, because the other chunks cannot be transmitted in this state.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5c9bada..c0f5616 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -1901,6 +1901,8 @@ struct sctp_association {
* after reaching 4294967295.
*/
__u32 addip_serial;
+ union sctp_addr *asconf_addr_del_pending;
+ int src_out_of_asoc_ok;
/* SCTP AUTH: list of the endpoint shared keys. These
* keys are provided out of band by the user applicaton
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 1a21c57..ea56b0d 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -279,6 +279,8 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->peer.asconf_capable = 0;
if (sctp_addip_noauth)
asoc->peer.asconf_capable = 1;
+ asoc->asconf_addr_del_pending = NULL;
+ asoc->src_out_of_asoc_ok = 0;
/* Create an input queue. */
sctp_inq_init(&asoc->base.inqueue);
@@ -443,6 +445,10 @@ void sctp_association_free(struct sctp_association *asoc)
asoc->peer.transport_count = 0;
+ /* Free pending address space being deleted */
+ if (asoc->asconf_addr_del_pending != NULL)
+ kfree(asoc->asconf_addr_del_pending);
+
/* Free any cached ASCONF_ACK chunk. */
sctp_assoc_free_asconf_acks(asoc);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 321f175..e67cc31 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -332,6 +332,13 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk,
matchlen = bmatchlen;
}
}
+ if (laddr->state == SCTP_ADDR_NEW && asoc->src_out_of_asoc_ok) {
+ bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
+ if (!baddr || (matchlen < bmatchlen)) {
+ baddr = &laddr->a;
+ matchlen = bmatchlen;
+ }
+ }
}
if (baddr) {
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1c88c89..7981854 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -754,6 +754,16 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
+ /* RFC 5061, 5.3
+ * F1) This means that until such time as the ASCONF
+ * containing the add is acknowledged, the sender MUST
+ * NOT use the new IP address as a source for ANY SCTP
+ * packet except on carrying an ASCONF Chunk.
+ */
+ if (asoc->src_out_of_asoc_ok &&
+ chunk->chunk_hdr->type != SCTP_CID_ASCONF)
+ continue;
+
list_del_init(&chunk->list);
/* Pick the right transport to use. */
@@ -881,6 +891,9 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
}
}
+ if (q->asoc->src_out_of_asoc_ok)
+ goto sctp_flush_out;
+
/* Is it OK to send data chunks? */
switch (asoc->state) {
case SCTP_STATE_COOKIE_ECHOED:
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d..40aef4d 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -510,7 +510,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
- if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
+ if (!laddr->valid || (laddr->state == SCTP_ADDR_DEL) ||
+ (laddr->state != SCTP_ADDR_SRC &&
+ !asoc->src_out_of_asoc_ok))
continue;
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 58eb27f..53e5ea2 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -2768,6 +2768,12 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
int addr_param_len = 0;
int totallen = 0;
int i;
+ sctp_addip_param_t del_param; /* 8 Bytes (Type 0xC002, Len and CrrID) */
+ struct sctp_af *del_af;
+ int del_addr_param_len = 0;
+ int del_paramlen = sizeof(sctp_addip_param_t);
+ union sctp_addr_param del_addr_param; /* (v4) 8 Bytes, (v6) 20 Bytes */
+ int del_pickup = 0;
/* Get total length of all the address parameters. */
addr_buf = addrs;
@@ -2780,6 +2786,17 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
totallen += addr_param_len;
addr_buf += af->sockaddr_len;
+ if (asoc->asconf_addr_del_pending && !del_pickup) {
+ if (!sctp_in_scope(asoc->asconf_addr_del_pending,
+ sctp_scope(addr)))
+ continue;
+ /* reuse the parameter length from the same scope one */
+ totallen += paramlen;
+ totallen += addr_param_len;
+ del_pickup = 1;
+ asoc->src_out_of_asoc_ok = 1;
+ SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen);
+ }
}
/* Create an asconf chunk with the required length. */
@@ -2802,6 +2819,19 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc,
addr_buf += af->sockaddr_len;
}
+ if (flags == SCTP_PARAM_ADD_IP && del_pickup) {
+ addr = asoc->asconf_addr_del_pending;
+ del_af = sctp_get_af_specific(addr->v4.sin_family);
+ del_addr_param_len = del_af->to_addr_param(addr,
+ &del_addr_param);
+ del_param.param_hdr.type = SCTP_PARAM_DEL_IP;
+ del_param.param_hdr.length = htons(del_paramlen +
+ del_addr_param_len);
+ del_param.crr_id = i;
+
+ sctp_addto_chunk(retval, del_paramlen, &del_param);
+ sctp_addto_chunk(retval, del_addr_param_len, &del_addr_param);
+ }
return retval;
}
@@ -3224,6 +3254,11 @@ static void sctp_asconf_param_success(struct sctp_association *asoc,
case SCTP_PARAM_DEL_IP:
local_bh_disable();
sctp_del_bind_addr(bp, &addr);
+ if (asoc->asconf_addr_del_pending != NULL &&
+ sctp_cmp_addr_exact(asoc->asconf_addr_del_pending, &addr)) {
+ kfree(asoc->asconf_addr_del_pending);
+ asoc->asconf_addr_del_pending = NULL;
+ }
local_bh_enable();
list_for_each_entry(transport, &asoc->peer.transport_addr_list,
transports) {
@@ -3381,6 +3416,9 @@ int sctp_process_asconf_ack(struct sctp_association *asoc,
asconf_len -= length;
}
+ if (no_err && asoc->src_out_of_asoc_ok)
+ asoc->src_out_of_asoc_ok = 0;
+
/* Free the cached last sent asconf chunk. */
list_del_init(&asconf->transmitted_list);
sctp_chunk_free(asconf);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..7d2fd48 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -583,10 +583,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
goto out;
}
- retval = sctp_send_asconf(asoc, chunk);
- if (retval)
- goto out;
-
/* Add the new addresses to the bind address list with
* use_as_src set to 0.
*/
@@ -599,6 +595,23 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
+ if (asoc->src_out_of_asoc_ok) {
+ struct sctp_transport *trans;
+
+ list_for_each_entry(trans,
+ &asoc->peer.transport_addr_list, transports) {
+ /* Clear the source and route cache */
+ dst_release(trans->dst);
+ trans->cwnd = min(4*asoc->pathmtu, max_t(__u32,
+ 2*asoc->pathmtu, 4380));
+ trans->ssthresh = asoc->peer.i.a_rwnd;
+ trans->rto = asoc->rto_initial;
+ trans->rtt = trans->srtt = trans->rttvar = 0;
+ sctp_transport_route(trans, NULL,
+ sctp_sk(asoc->base.sk));
+ }
+ }
+ retval = sctp_send_asconf(asoc, chunk);
}
out:
@@ -715,7 +728,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
struct sctp_sockaddr_entry *saddr;
int i;
int retval = 0;
+ int stored = 0;
+ chunk = NULL;
if (!sctp_addip_enable)
return retval;
@@ -766,8 +781,32 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp);
- if (!laddr)
- continue;
+ if ((laddr == NULL) && (addrcnt == 1)) {
+ if (asoc->asconf_addr_del_pending)
+ continue;
+ asoc->asconf_addr_del_pending =
+ kzalloc(sizeof(union sctp_addr), GFP_ATOMIC);
+ asoc->asconf_addr_del_pending->sa.sa_family =
+ addrs->sa_family;
+ asoc->asconf_addr_del_pending->v4.sin_port =
+ htons(bp->port);
+ if (addrs->sa_family == AF_INET) {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)addrs;
+ asoc->asconf_addr_del_pending->v4.sin_addr.s_addr = sin->sin_addr.s_addr;
+ } else if (addrs->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)addrs;
+ ipv6_addr_copy(&asoc->asconf_addr_del_pending->v6.sin6_addr, &sin6->sin6_addr);
+ }
+ SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ",
+ " at %p\n", asoc, asoc->asconf_addr_del_pending,
+ asoc->asconf_addr_del_pending);
+ stored = 1;
+ goto skip_mkasconf;
+ }
/* We do not need RCU protection throughout this loop
* because this is done under a socket lock from the
@@ -780,6 +819,7 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
goto out;
}
+skip_mkasconf:
/* Reset use_as_src flag for the addresses in the bind address
* list that are to be deleted.
*/
@@ -805,6 +845,9 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
sctp_sk(asoc->base.sk));
}
+ if (stored)
+ /* We don't need to transmit ASCONF */
+ continue;
retval = sctp_send_asconf(asoc, chunk);
}
out:
^ permalink raw reply related
* [PATCH net-next-2.6 v6 4/5] sctp: Add ADD/DEL ASCONF handling at the receiver
From: Michio Honda @ 2011-04-26 4:30 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
This patch fixes the problem that the original code cannot delete the remote address where the corresponding transport is currently directed, even when the ASCONF is sent from the other address (this situation happens when the single-homed sender transmits ASCONF with ADD and DEL.)
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 58eb27f..3740603 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3014,7 +3014,7 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
* an Error Cause TLV set to the new error code 'Request to
* Delete Source IP Address'
*/
- if (sctp_cmp_addr_exact(sctp_source(asconf), &addr))
+ if (sctp_cmp_addr_exact(&asconf->source, &addr))
return SCTP_ERROR_DEL_SRC_IP;
/* Section 4.2.2
^ permalink raw reply related
* [PATCH net-next-2.6 v6 3/5] sctp: Add socket option operation for Auto-ASCONF
From: Michio Honda @ 2011-04-26 4:30 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
This patch allows the application to operate Auto-ASCONF on/off behavior via setsockopt() and getsockopt().
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 32fd512..0842ef0 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -92,6 +92,7 @@ typedef __s32 sctp_assoc_t;
#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */
#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */
#define SCTP_GET_ASSOC_ID_LIST 29 /* Read only */
+#define SCTP_AUTO_ASCONF 30
/* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options.
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..bf3501c 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -3335,6 +3335,46 @@ static int sctp_setsockopt_del_key(struct sock *sk,
}
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ *
+ * This option will enable or disable the use of the automatic generation of
+ * ASCONF chunks to add and delete addresses to an existing association. Note
+ * that this option has two caveats namely: a) it only affects sockets that
+ * are bound to all addresses available to the SCTP stack, and b) the system
+ * administrator may have an overriding control that turns the ASCONF feature
+ * off no matter what setting the socket option may have.
+ * This option expects an integer boolean flag, where a non-zero value turns on
+ * the option, and a zero value turns off the option.
+ * Note. In this implementation, socket operation overrides default parameter
+ * being set by sysctl as well as FreeBSD implementation
+ */
+static int sctp_setsockopt_auto_asconf(struct sock *sk, char __user *optval,
+ unsigned int optlen)
+{
+ int val;
+ struct sctp_sock *sp = sctp_sk(sk);
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+ if (!sctp_is_ep_boundall(sk) && val)
+ return -EINVAL;
+ if ((val && sp->do_auto_asconf) || (!val && !sp->do_auto_asconf))
+ return 0;
+
+ if (val == 0 && sp->do_auto_asconf) {
+ list_del(&sp->auto_asconf_list);
+ sp->do_auto_asconf = 0;
+ } else if (val && !sp->do_auto_asconf) {
+ list_add_tail(&sp->auto_asconf_list,
+ &sctp_auto_asconf_splist);
+ sp->do_auto_asconf = 1;
+ }
+ return 0;
+}
+
/* API 6.2 setsockopt(), getsockopt()
*
@@ -3482,6 +3522,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
case SCTP_AUTH_DELETE_KEY:
retval = sctp_setsockopt_del_key(sk, optval, optlen);
break;
+ case SCTP_AUTO_ASCONF:
+ retval = sctp_setsockopt_auto_asconf(sk, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
@@ -5277,6 +5320,28 @@ static int sctp_getsockopt_assoc_number(struct sock *sk, int len,
return 0;
}
+/*
+ * 8.1.23 SCTP_AUTO_ASCONF
+ * See the corresponding setsockopt entry as description
+ */
+static int sctp_getsockopt_auto_asconf(struct sock *sk, int len,
+ char __user *optval, int __user *optlen)
+{
+ int val = 0;
+
+ if (len < sizeof(int))
+ return -EINVAL;
+
+ len = sizeof(int);
+ if (sctp_sk(sk)->do_auto_asconf && sctp_is_ep_boundall(sk))
+ val = 1;
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+ return 0;
+}
+
/*
* 8.2.6. Get the Current Identifiers of Associations
* (SCTP_GET_ASSOC_ID_LIST)
@@ -5461,6 +5526,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_GET_ASSOC_ID_LIST:
retval = sctp_getsockopt_assoc_ids(sk, len, optval, optlen);
break;
+ case SCTP_AUTO_ASCONF:
+ retval = sctp_getsockopt_auto_asconf(sk, len, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
^ permalink raw reply related
* [PATCH net-next-2.6 v6 2/5] sctp: Add sysctl support for Auto-ASCONF
From: Michio Honda @ 2011-04-26 4:29 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
This patch allows the system administrator to change default Auto-ASCONF on/off behavior via an sysctl value.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 50cb57f..6b39529 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -183,6 +183,13 @@ static ctl_table sctp_table[] = {
.proc_handler = proc_dointvec,
},
{
+ .procname = "default_auto_asconf",
+ .data = &sctp_default_auto_asconf,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "prsctp_enable",
.data = &sctp_prsctp_enable,
.maxlen = sizeof(int),
^ permalink raw reply related
* [PATCH net-next-2.6 v6 1/5] sctp: Add Auto-ASCONF support
From: Michio Honda @ 2011-04-26 4:29 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
SCTP reconfigure the IP addresses in the association by using ASCONF chunks as mentioned in RFC5061.
For example, we can start to use the newly configured IP address in the existing association.
ASCONF operation is invoked in two ways:
First is done by the application to call sctp_bindx() system call.
Second is automatic operation in the SCTP stack with address events in the host computer (called auto_asconf) .
The former is already implemented, and this patch implement the latter.
Signed-off-by: Michio Honda <micchie@sfc.wide.ad.jp>
---
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 7e8e34c..1aa1a58 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -121,6 +121,7 @@ extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
int flags);
extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
+void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int);
/*
* sctp/socket.c
@@ -135,6 +136,7 @@ void sctp_sock_rfree(struct sk_buff *skb);
void sctp_copy_sock(struct sock *newsk, struct sock *sk,
struct sctp_association *asoc);
extern struct percpu_counter sctp_sockets_allocated;
+int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);
/*
* sctp/primitive.c
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5c9bada..f7f6add 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -205,6 +205,11 @@ extern struct sctp_globals {
* It is a list of sctp_sockaddr_entry.
*/
struct list_head local_addr_list;
+ int default_auto_asconf;
+ struct list_head addr_waitq;
+ struct timer_list addr_wq_timer;
+ struct list_head auto_asconf_splist;
+ spinlock_t addr_wq_lock;
/* Lock that protects the local_addr_list writers */
spinlock_t addr_list_lock;
@@ -264,6 +269,11 @@ extern struct sctp_globals {
#define sctp_port_hashtable (sctp_globals.port_hashtable)
#define sctp_local_addr_list (sctp_globals.local_addr_list)
#define sctp_local_addr_lock (sctp_globals.addr_list_lock)
+#define sctp_auto_asconf_splist (sctp_globals.auto_asconf_splist)
+#define sctp_addr_waitq (sctp_globals.addr_waitq)
+#define sctp_addr_wq_timer (sctp_globals.addr_wq_timer)
+#define sctp_addr_wq_lock (sctp_globals.addr_wq_lock)
+#define sctp_default_auto_asconf (sctp_globals.default_auto_asconf)
#define sctp_scope_policy (sctp_globals.ipv4_scope_policy)
#define sctp_addip_enable (sctp_globals.addip_enable)
#define sctp_addip_noauth (sctp_globals.addip_noauth_enable)
@@ -341,6 +351,8 @@ struct sctp_sock {
atomic_t pd_mode;
/* Receive to here while partial delivery is in effect. */
struct sk_buff_head pd_lobby;
+ struct list_head auto_asconf_list;
+ int do_auto_asconf;
};
static inline struct sctp_sock *sctp_sk(const struct sock *sk)
@@ -796,6 +808,8 @@ struct sctp_sockaddr_entry {
__u8 valid;
};
+#define SCTP_ADDRESS_TICK_DELAY 500
+
typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *);
/* This structure holds lists of chunks as we are assembling for
@@ -1239,6 +1253,7 @@ sctp_scope_t sctp_scope(const union sctp_addr *);
int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
int sctp_is_any(struct sock *sk, const union sctp_addr *addr);
int sctp_addr_is_valid(const union sctp_addr *addr);
+int sctp_is_ep_boundall(struct sock *sk);
/* What type of endpoint? */
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index faf71d1..869267b 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -536,6 +536,21 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
return 0;
}
+int sctp_is_ep_boundall(struct sock *sk)
+{
+ struct sctp_bind_addr *bp;
+ struct sctp_sockaddr_entry *addr;
+
+ bp = &sctp_sk(sk)->ep->base.bind_addr;
+ if (sctp_list_single_entry(&bp->address_list)) {
+ addr = list_entry(bp->address_list.next,
+ struct sctp_sockaddr_entry, list);
+ if (sctp_is_any(sk, &addr->a))
+ return 1;
+ }
+ return 0;
+}
+
/********************************************************************
* 3rd Level Abstractions
********************************************************************/
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 321f175..233eb3a 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -105,6 +105,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
addr->valid = 1;
spin_lock_bh(&sctp_local_addr_lock);
list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
spin_unlock_bh(&sctp_local_addr_lock);
}
break;
@@ -115,6 +116,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
if (addr->a.sa.sa_family == AF_INET6 &&
ipv6_addr_equal(&addr->a.v6.sin6_addr,
&ifa->addr)) {
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
found = 1;
addr->valid = 0;
list_del_rcu(&addr->list);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d..6a08d63 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -636,6 +636,160 @@ static void sctp_v4_ecn_capable(struct sock *sk)
INET_ECN_xmit(sk);
}
+void sctp_addr_wq_timeout_handler(unsigned long arg)
+{
+ struct sctp_sockaddr_entry *addrw = NULL;
+ union sctp_addr *addr = NULL;
+ struct sctp_sock *sp = NULL;
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+retry_wq:
+ if (list_empty(&sctp_addr_waitq)) {
+ SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: nothing in addr waitq\n");
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+ addrw = list_first_entry(&sctp_addr_waitq, struct sctp_sockaddr_entry,
+ list);
+ addr = &addrw->a;
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
+ " for cmd %d at entry %p\n", &sctp_addr_waitq, addr, addrw->state,
+ addrw);
+
+ /* Now we send an ASCONF for each association */
+ /* Note. we currently don't handle link local IPv6 addressees */
+ if (addr->sa.sa_family == AF_INET6) {
+ struct in6_addr *in6 = (struct in6_addr *)&addr->v6.sin6_addr;
+
+ if (ipv6_addr_type(&addr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) {
+ SCTP_DEBUG_PRINTK("sctp_timo_handler: link local, hence don't tell sockets\n");
+ list_del(&addrw->list);
+ kfree(addrw);
+ goto retry_wq;
+ }
+ if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 &&
+ addrw->state == SCTP_ADDR_NEW) {
+ unsigned long timeo_val;
+
+ SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n",
+ SCTP_ADDRESS_TICK_DELAY);
+ timeo_val = jiffies;
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+ }
+ list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) {
+ struct sock *sk;
+
+ sk = sctp_opt2sk(sp);
+ /* ignore bound-specific endpoints */
+ if (!sctp_is_ep_boundall(sk))
+ continue;
+ sctp_bh_lock_sock(sk);
+ if (sctp_asconf_mgmt(sp, addrw) < 0) {
+ SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n");
+ sctp_bh_unlock_sock(sk);
+ continue;
+ }
+ sctp_bh_unlock_sock(sk);
+ }
+
+ list_del(&addrw->list);
+ kfree(addrw);
+
+ if (!list_empty(&sctp_addr_waitq))
+ goto retry_wq;
+
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
+static void sctp_free_addr_wq()
+{
+ struct sctp_sockaddr_entry *addrw = NULL;
+ struct sctp_sockaddr_entry *temp = NULL;
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+ del_timer(&sctp_addr_wq_timer);
+ list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
+ list_del(&addrw->list);
+ kfree(addrw);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
+/* lookup the entry for the same address in the addr_waitq
+ * sctp_addr_wq MUST be locked
+ */
+static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr)
+{
+ struct sctp_sockaddr_entry *addrw;
+
+ list_for_each_entry(addrw, &sctp_addr_waitq, list) {
+ if (addrw->a.sa.sa_family != addr->a.sa.sa_family)
+ continue;
+ if (addrw->a.sa.sa_family == AF_INET) {
+ if (addrw->a.v4.sin_addr.s_addr ==
+ addr->a.v4.sin_addr.s_addr)
+ return addrw;
+ } else if (addrw->a.sa.sa_family == AF_INET6) {
+ if (ipv6_addr_equal(&addrw->a.v6.sin6_addr,
+ &addr->a.v6.sin6_addr))
+ return addrw;
+ }
+ }
+ return NULL;
+}
+
+void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd)
+{
+ struct sctp_sockaddr_entry *addrw = NULL;
+ unsigned long timeo_val;
+ union sctp_addr *tmpaddr;
+
+ /* first, we check if an opposite message already exist in the queue.
+ * If we found such message, it is removed.
+ * This operation is a bit stupid, but the DHCP client attaches the
+ * new address after a couple of addition and deletion of that address
+ */
+
+ spin_lock_bh(&sctp_addr_wq_lock);
+ /* Offsets existing events in addr_wq */
+ addrw = sctp_addr_wq_lookup(addr);
+ tmpaddr = &addrw->a;
+ if (addrw) {
+ if (addrw->state != cmd) {
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
+ " in wq %p\n", addrw->state, tmpaddr,
+ &sctp_addr_waitq);
+ list_del(&addrw->list);
+ kfree(addrw);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+
+ /* OK, we have to add the new address to the wait queue */
+ addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
+ if (addrw == NULL) {
+ spin_unlock_bh(&sctp_addr_wq_lock);
+ return;
+ }
+ addrw->state = cmd;
+ list_add_tail(&addrw->list, &sctp_addr_waitq);
+ tmpaddr = &addrw->a;
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
+ " in wq %p\n", addrw->state, tmpaddr, &sctp_addr_waitq);
+
+ if (!timer_pending(&sctp_addr_wq_timer)) {
+ timeo_val = jiffies;
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
+ }
+ spin_unlock_bh(&sctp_addr_wq_lock);
+}
+
/* Event handler for inet 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
@@ -663,6 +817,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
addr->valid = 1;
spin_lock_bh(&sctp_local_addr_lock);
list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
spin_unlock_bh(&sctp_local_addr_lock);
}
break;
@@ -673,6 +828,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
if (addr->a.sa.sa_family == AF_INET &&
addr->a.v4.sin_addr.s_addr ==
ifa->ifa_local) {
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
found = 1;
addr->valid = 0;
list_del_rcu(&addr->list);
@@ -1256,6 +1412,7 @@ SCTP_STATIC __init int sctp_init(void)
/* Disable ADDIP by default. */
sctp_addip_enable = 0;
sctp_addip_noauth = 0;
+ sctp_default_auto_asconf = 0;
/* Enable PR-SCTP by default. */
sctp_prsctp_enable = 1;
@@ -1280,6 +1437,13 @@ SCTP_STATIC __init int sctp_init(void)
spin_lock_init(&sctp_local_addr_lock);
sctp_get_local_addr_list();
+ /* Initialize the address event list */
+ INIT_LIST_HEAD(&sctp_addr_waitq);
+ INIT_LIST_HEAD(&sctp_auto_asconf_splist);
+ spin_lock_init(&sctp_addr_wq_lock);
+ sctp_addr_wq_timer.expires = 0;
+ setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0);
+
status = sctp_v4_protosw_init();
if (status)
@@ -1351,6 +1515,7 @@ SCTP_STATIC __exit void sctp_exit(void)
/* Unregister with inet6/inet layers. */
sctp_v6_del_protocol();
sctp_v4_del_protocol();
+ sctp_free_addr_wq();
/* Free the control endpoint. */
inet_ctl_sock_destroy(sctp_ctl_sock);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..fb183c5 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -811,6 +811,37 @@ out:
return retval;
}
+/* set addr events to assocs in the endpoint. ep and addr_wq must be locked */
+int
+sctp_asconf_mgmt(struct sctp_sock *sp, struct sctp_sockaddr_entry *addrw)
+{
+ struct sock *sk = sctp_opt2sk(sp);
+ union sctp_addr *addr = NULL;
+ int cmd;
+ int error = 0;
+
+ addr = &addrw->a;
+ addr->v4.sin_port = htons(sp->ep->base.bind_addr.port);
+ cmd = addrw->state;
+
+ SCTP_DEBUG_PRINTK("sctp_asconf_mgmt sp:%p\n", sp);
+ if (cmd == SCTP_ADDR_NEW) {
+ error = sctp_send_asconf_add_ip(sk, (struct sockaddr *)addr, 1);
+ if (error) {
+ SCTP_DEBUG_PRINTK("asconf_mgmt: send_asconf_add_ip returns %d\n", error);
+ return error;
+ }
+ } else if (cmd == SCTP_ADDR_DEL) {
+ error = sctp_send_asconf_del_ip(sk, (struct sockaddr *)addr, 1);
+ if (error) {
+ SCTP_DEBUG_PRINTK("asconf_mgmt: send_asconf_del_ip returns %d\n", error);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
/* Helper for tunneling sctp_bindx() requests through sctp_setsockopt()
*
* API 8.1
@@ -3764,6 +3795,13 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
local_bh_disable();
percpu_counter_inc(&sctp_sockets_allocated);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ if (sctp_default_auto_asconf) {
+ list_add_tail(&sp->auto_asconf_list,
+ &sctp_auto_asconf_splist);
+ sp->do_auto_asconf = 1;
+ } else
+ sp->do_auto_asconf = 0;
+ SCTP_DEBUG_PRINTK("sctp_init_sk sk:%p ep:%p\n", sk, ep);
local_bh_enable();
return 0;
@@ -3773,11 +3811,17 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
SCTP_STATIC void sctp_destroy_sock(struct sock *sk)
{
struct sctp_endpoint *ep;
+ struct sctp_sock *sp;
SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
/* Release our hold on the endpoint. */
- ep = sctp_sk(sk)->ep;
+ sp = sctp_sk(sk);
+ ep = sp->ep;
+ if (sp->do_auto_asconf) {
+ sp->do_auto_asconf = 0;
+ list_del(&sp->auto_asconf_list);
+ }
sctp_endpoint_free(ep);
local_bh_disable();
percpu_counter_dec(&sctp_sockets_allocated);
^ permalink raw reply related
* [PATCH net-next-2.6 v6 0/5] sctp: Patch series
From: Michio Honda @ 2011-04-26 4:28 UTC (permalink / raw)
To: netdev; +Cc: lksctp-developers
Series of 5 patches to support auto_asconf and the other related functionalities that auto_asconf relies on.
Cheers,
- Michio
[1/5] Add Auto-ASCONF support
[2/5] Add sysctl support for Auto-ASCONF
[3/5] Add socket option operation for Auto-ASCONF
[4/5] Add ADD/DEL ASCONF handling at the receiver
[5/5] Add ASCONF operation on the single-homed host
^ permalink raw reply
* Re: [Bugme-new] [Bug 33842] New: NULL pointer dereference in ip_fragment
From: Andrew Morton @ 2011-04-26 4:29 UTC (permalink / raw)
To: netdev; +Cc: bugzilla-daemon, bugme-daemon, tom
In-Reply-To: <bug-33842-10286@https.bugzilla.kernel.org/>
(switched to email. Please respond via emailed reply-to-all, not via the
bugzilla web interface).
On Sat, 23 Apr 2011 07:51:56 GMT bugzilla-daemon@bugzilla.kernel.org wrote:
> https://bugzilla.kernel.org/show_bug.cgi?id=33842
>
> Summary: NULL pointer dereference in ip_fragment
oops in ip_defragment(). Kernel is 2.6.39-rc4. There are some
screenshots attached to the report.
> Product: Networking
> Version: 2.5
> Platform: All
> OS/Version: Linux
> Tree: Mainline
> Status: NEW
> Severity: normal
> Priority: P1
> Component: Other
> AssignedTo: acme@ghostprotocols.net
> ReportedBy: tom@dbservice.com
> Regression: No
>
>
> The host is using the ath9k driver. eth0+wlan0 are bridged. Shortly after I
> start using the wireless network with my macbook, the bug triggers. No idea if
> it's wireless related, because there's also a rtl8169_rx_interrupt entry in the
> stacktrace.
>
> This is a transcript, since I don't (have/know of) any way to get the backtrace
> out of a crashed box.
>
> IP: ip_fragment+0x52/0x840
> Call Trace:
> <IRQ>
> br_parse_ip_options
> br_flood_deliver
> br_parse_ip_options
> br_nf_dev_queue_xmit
> br_nf_post_routing
> nf_iterate
>
> then also:
> lots of br_flood_deliver
> lots of br_*_finish
> one ? rtl8169_interrupt
> one ? ath9k_ioread32
>
^ permalink raw reply
* linux-next: build failure after merge of the net tree
From: Stephen Rothwell @ 2011-04-26 3:51 UTC (permalink / raw)
To: David Miller, netdev
Cc: linux-next, linux-kernel, hayeswang,
"François Romieu"
Hi all,
After merging the net tree, today's linux-next build (x86_64 allmodconfig)
failed like this:
drivers/net/r8169.c: In function 'rtl8168e_1_hw_phy_config':
drivers/net/r8169.c:2578: error: too many arguments to function 'rtl_apply_firmware'
drivers/net/r8169.c:2578: error: void value not ignored as it ought to be
drivers/net/r8169.c: In function 'rtl8168e_2_hw_phy_config':
drivers/net/r8169.c:2586: error: too many arguments to function 'rtl_apply_firmware'
drivers/net/r8169.c:2586: error: void value not ignored as it ought to be
Caused by commit 953a12cc2889 ("r8169: don't request firmware when
there's no userspace") from the net-current tree interacting with commit
01dc7fec4025 ("net/r8169: support RTL8168E").
I applied the following fixup patch:
From: Stephen Rothwell <sfr@canb.auug.org.au>
Date: Tue, 26 Apr 2011 13:47:51 +1000
Subject: [PATCH] r8169: fix for rtl_apply_firmware API change
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
drivers/net/r8169.c | 10 +++++-----
1 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c
index 3f93fc7..c3e264f 100644
--- a/drivers/net/r8169.c
+++ b/drivers/net/r8169.c
@@ -190,7 +190,9 @@ static const struct rtl_firmware_info {
{ .mac_version = RTL_GIGA_MAC_VER_25, .fw_name = FIRMWARE_8168D_1 },
{ .mac_version = RTL_GIGA_MAC_VER_26, .fw_name = FIRMWARE_8168D_2 },
{ .mac_version = RTL_GIGA_MAC_VER_29, .fw_name = FIRMWARE_8105E_1 },
- { .mac_version = RTL_GIGA_MAC_VER_30, .fw_name = FIRMWARE_8105E_1 }
+ { .mac_version = RTL_GIGA_MAC_VER_30, .fw_name = FIRMWARE_8105E_1 },
+ { .mac_version = RTL_GIGA_MAC_VER_31, .fw_name = FIRMWARE_8168E_1 },
+ { .mac_version = RTL_GIGA_MAC_VER_32, .fw_name = FIRMWARE_8168E_2 }
};
enum cfg_version {
@@ -2575,16 +2577,14 @@ static void rtl8168e_hw_phy_config(struct rtl8169_private *tp)
static void rtl8168e_1_hw_phy_config(struct rtl8169_private *tp)
{
- if (rtl_apply_firmware(tp, FIRMWARE_8168E_1) < 0)
- netif_warn(tp, probe, tp->dev, "unable to apply firmware patch\n");
+ rtl_apply_firmware(tp);
rtl8168e_hw_phy_config(tp);
}
static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp)
{
- if (rtl_apply_firmware(tp, FIRMWARE_8168E_2) < 0)
- netif_warn(tp, probe, tp->dev, "unable to apply firmware patch\n");
+ rtl_apply_firmware(tp);
rtl8168e_hw_phy_config(tp);
}
--
1.7.4.4
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
^ permalink raw reply related
* Re: how to set vlan filter for intel 82599
From: Ben Hutchings @ 2011-04-26 3:51 UTC (permalink / raw)
To: zhou rui; +Cc: netdev
In-Reply-To: <BANLkTikav_NimAoFj_2Wo-BMD-3uVjU--w@mail.gmail.com>
On Tue, 2011-04-26 at 11:39 +0800, zhou rui wrote:
> On Tue, Apr 26, 2011 at 10:57 AM, Ben Hutchings
> <bhutchings@solarflare.com> wrote:
> > On Tue, 2011-04-26 at 10:19 +0800, zhou rui wrote:
> >> hi
> >> here is the problem troubles me,how to set vlan filter for intel
> >> 82599? for example
> >> I want vlan id 0~31 will go to queue 0, vlan id 32-63 will go to queue
> >> 1...below is my setting,but doesn't work
> >>
> >> don't know the exact meanning of the vlan-mask and vlan,how are they calculated?
> >>
> >> ./ethtool -K eth5 ntuple on
> >>
> >> ./ethtool -U eth5 flow-type udp4 src-ip 0x0 src-ip-mask 0x0 dst-ip 0x0
> >> dst-ip-mask 0x0 src-port 0x0 src-port-mask 0x0 dst-port 0x0
> >> dst-port-mask 0x0 vlan 0x0000 vlan-mask 0x00E0 user-def 0x0
> >> user-def-mask 0x0 action 0
> > [...]
> >
> > This specifies a filter for UDP/IPv4 packets, and the masks are wrong.
> > If you actually wanted to filter only UDP/IPv4 packets for VID 0-31 then
> > the correct syntax would be:
> >
> > ethtool -U eth5 flow-type udp4 vlan 0 vlan-mask 0xf01f
> >
> > If you don't care about the layer 3/4 protocols then you would need to
> > use 'flow-type ether', but no driver implements that yet. (Well, sfc
> > implements the *type*, but not filtering by VID only.)
[...]
> hi ben,thanks for your help,would you mind tell me "32~63" VID filter?
> still can not understand the vlan-mask
The vlan-mask specifies tag bits to be ignored. You want to ignore the
priority, CFI and lower 5 bits of the VID, hence 0xf01f. For a
different group of 32 VIDs you would only change the vlan argument, not
the vlan-mask argument.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* [PATCH net-next-2.6 7/7] sctp: fix IPv6 source address output routing with IPsec
From: Wei Yongjun @ 2011-04-26 3:49 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
When do IPv6 source address output routing, the flowi6_proto
field is missing, different router will be return under IPsec.
This patch fixed it.
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
net/sctp/ipv6.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index cbbfe20..180495e 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -265,6 +265,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
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;
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 6/7] sctp: clean up IPv6 route and XFRM lookups
From: Wei Yongjun @ 2011-04-26 3:48 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
Using ip6_dst_lookup_flow() instead of ip6_dst_lookup()
and then do xfrm_lookup().
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
net/sctp/ipv6.c | 40 +++++++++-------------------------------
1 files changed, 9 insertions(+), 31 deletions(-)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 60601f3..cbbfe20 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -244,30 +244,6 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
return ip6_xmit(sk, skb, &fl6, np->opt);
}
-/* Small helper function that combines route and XFRM lookups. This is
- * done since we might be looping through route lookups.
- */
-static int sctp_v6_dst_lookup(struct sock *sk, struct dst_entry **dst,
- struct flowi6 *fl6)
-{
- int err;
-
- err = ip6_dst_lookup(sk, dst, fl6);
- if (err)
- goto done;
-
- err = xfrm_lookup(sock_net(sk), *dst, flowi6_to_flowi(fl6), sk, 0);
- if (err)
- goto done;
-
- return 0;
-
-done:
- dst_release(*dst);
- *dst = NULL;
- return err;
-}
-
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
@@ -285,7 +261,6 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
__u8 matchlen = 0;
__u8 bmatchlen;
sctp_scope_t scope;
- int err = 0;
memset(fl6, 0, sizeof(struct flowi6));
ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
@@ -304,7 +279,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- err = sctp_v6_dst_lookup(sk, &dst, fl6);
+ dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
+
if (!asoc || saddr)
goto out;
@@ -313,7 +289,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
/* ip6_dst_lookup has filled in the fl6->saddr for us. Check
* to see if we can use it.
*/
- if (!err) {
+ 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.
*/
@@ -359,18 +335,20 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
if (baddr) {
ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
fl6->fl6_sport = baddr->v6.sin6_port;
- err = sctp_v6_dst_lookup(sk, &dst, fl6);
+ dst = ip6_dst_lookup_flow(sk, fl6, NULL, false);
}
out:
- t->dst = dst;
- if (dst) {
+ 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, &fl6->saddr);
- } else
+ } else {
+ t->dst = NULL;
SCTP_DEBUG_PRINTK("NO ROUTE\n");
+ }
}
/* Returns the number of consecutive initial bits that match in the 2 ipv6
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 5/7] sctp: clean up route lookup calls
From: Wei Yongjun @ 2011-04-26 3:48 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
Change the call to take the transport parameter and set the
cached 'dst' appropriately inside the get_dst() function calls.
This will allow us in the future to clean up source address
storage as well.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 3 +--
net/sctp/ipv6.c | 19 +++++++------------
net/sctp/protocol.c | 12 +++++-------
net/sctp/transport.c | 23 ++++++++++-------------
4 files changed, 23 insertions(+), 34 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index cd4aea9..c9c0021 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -564,8 +564,7 @@ struct sctp_af {
int optname,
char __user *optval,
int __user *optlen);
- struct dst_entry *(*get_dst) (struct sctp_association *asoc,
- union sctp_addr *daddr,
+ void (*get_dst) (struct sctp_transport *t,
union sctp_addr *saddr,
struct flowi *fl,
struct sock *sk);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 31b6f35..60601f3 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -271,17 +271,16 @@ done:
/* 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,
- struct flowi *fl,
- struct sock *sk)
+static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
+ struct flowi *fl, struct sock *sk)
{
+ 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;
@@ -294,7 +293,6 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
fl6->flowi6_oif = daddr->v6.sin6_scope_id;
-
SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr);
if (asoc)
@@ -365,17 +363,14 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
}
out:
+ t->dst = dst;
if (dst) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
&rt->rt6i_dst.addr, &fl6->saddr);
- return dst;
- }
- SCTP_DEBUG_PRINTK("NO ROUTE\n");
- if (dst)
- dst_release(dst);
- return NULL;
+ } else
+ SCTP_DEBUG_PRINTK("NO ROUTE\n");
}
/* Returns the number of consecutive initial bits that match in the 2 ipv6
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 92b8b91..38115ce 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -463,17 +463,16 @@ 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,
- struct flowi *fl,
- struct sock *sk)
+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 = &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));
@@ -548,13 +547,12 @@ 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);
else
SCTP_DEBUG_PRINTK("NO ROUTE\n");
-
- return dst;
}
/* For v4, the source address is cached in the route entry(dst). So no need
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 1fbb920..d8595dd 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -213,17 +213,17 @@ void sctp_transport_set_owner(struct sctp_transport *transport,
/* Initialize the pmtu of a transport. */
void sctp_transport_pmtu(struct sctp_transport *transport, struct sock *sk)
{
- struct dst_entry *dst;
struct flowi fl;
- dst = transport->af_specific->get_dst(transport->asoc,
- &transport->ipaddr,
- &transport->saddr,
+ /* 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,
&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;
}
@@ -274,12 +274,9 @@ 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;
struct flowi fl;
- dst = af->get_dst(asoc, daddr, saddr, &fl, sctp_opt2sk(opt));
- transport->dst = dst;
+ af->get_dst(transport, saddr, &fl, sctp_opt2sk(opt));
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
@@ -289,8 +286,8 @@ void sctp_transport_route(struct sctp_transport *transport,
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().
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 4/7] sctp: remove useless arguments from get_saddr() call
From: Wei Yongjun @ 2011-04-26 3:47 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
There is no point in passing a destination address to
a get_saddr() call.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 1 -
net/sctp/ipv6.c | 2 +-
net/sctp/protocol.c | 1 -
net/sctp/transport.c | 2 +-
4 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index a98d36f..cd4aea9 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -571,7 +571,6 @@ struct sctp_af {
struct sock *sk);
void (*get_saddr) (struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 74fe28c..31b6f35 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -392,11 +392,11 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
*/
static void sctp_v6_get_saddr(struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl)
{
struct flowi6 *fl6 = &fl->u.ip6;
union sctp_addr *saddr = &t->saddr;
+ union sctp_addr *daddr = &t->ipaddr;
SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p daddr:%pI6 ",
__func__, t->asoc, t->dst, &daddr->v6.sin6_addr);
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 7be5df6..92b8b91 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -562,7 +562,6 @@ out:
*/
static void sctp_v4_get_saddr(struct sctp_sock *sk,
struct sctp_transport *t,
- union sctp_addr *daddr,
struct flowi *fl)
{
union sctp_addr *saddr = &t->saddr;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 2544b9b..1fbb920 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -284,7 +284,7 @@ void sctp_transport_route(struct sctp_transport *transport,
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
else
- af->get_saddr(opt, transport, daddr, &fl);
+ af->get_saddr(opt, transport, &fl);
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
return;
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 3/7] sctp: make sctp over IPv6 work with IPsec
From: Wei Yongjun @ 2011-04-26 3:47 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
SCTP never called xfrm_output after it's v6 route lookups so
that never really worked with ipsec. Additioanlly, we never
passed port nubmers in the flowi, so any port based policies were
never applied as well. Now that we can fixed ipv6 routing lookup
code, we can add xfrm_output calls and pass port numbers.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
net/sctp/ipv6.c | 34 ++++++++++++++++++++++++++++++++--
1 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index cc9ea37..74fe28c 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -244,6 +244,30 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
return ip6_xmit(sk, skb, &fl6, np->opt);
}
+/* Small helper function that combines route and XFRM lookups. This is
+ * done since we might be looping through route lookups.
+ */
+static int sctp_v6_dst_lookup(struct sock *sk, struct dst_entry **dst,
+ struct flowi6 *fl6)
+{
+ int err;
+
+ err = ip6_dst_lookup(sk, dst, fl6);
+ if (err)
+ goto done;
+
+ err = xfrm_lookup(sock_net(sk), *dst, flowi6_to_flowi(fl6), sk, 0);
+ if (err)
+ goto done;
+
+ return 0;
+
+done:
+ dst_release(*dst);
+ *dst = NULL;
+ return err;
+}
+
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
@@ -266,18 +290,23 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
memset(fl6, 0, sizeof(struct flowi6));
ipv6_addr_copy(&fl6->daddr, &daddr->v6.sin6_addr);
+ fl6->fl6_dport = daddr->v6.sin6_port;
if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
fl6->flowi6_oif = daddr->v6.sin6_scope_id;
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);
+ fl6->fl6_sport = saddr->v6.sin6_port;
SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- err = ip6_dst_lookup(sk, &dst, fl6);
+ err = sctp_v6_dst_lookup(sk, &dst, fl6);
if (!asoc || saddr)
goto out;
@@ -331,7 +360,8 @@ static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
rcu_read_unlock();
if (baddr) {
ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
- err = ip6_dst_lookup(sk, &dst, fl6);
+ fl6->fl6_sport = baddr->v6.sin6_port;
+ err = sctp_v6_dst_lookup(sk, &dst, fl6);
}
out:
--
1.6.5.2
^ permalink raw reply related
* [PATCH net-next-2.6 2/7] sctp: cache the ipv6 source after route lookup
From: Wei Yongjun @ 2011-04-26 3:46 UTC (permalink / raw)
To: David Miller; +Cc: netdev@vger.kernel.org, lksctp
In-Reply-To: <4DB63F85.2090609@cn.fujitsu.com>
From: Vlad Yasevich <vladislav.yasevich@hp.com>
The ipv6 routing lookup does give us a source address,
but instead of filling it into the dst, it's stored in
the flowi. We can use that instead of going through the
entire source address selection again. Now we pass
the flowi around to get at the source for ipv6, but let
ipv4 still deal with dst.
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
---
include/net/sctp/structs.h | 13 ++--
net/sctp/ipv6.c | 163 +++++++++++++++++++++-----------------------
net/sctp/protocol.c | 50 +++++++-------
net/sctp/socket.c | 2 +-
net/sctp/transport.c | 15 +++--
5 files changed, 119 insertions(+), 124 deletions(-)
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 5c9bada..a98d36f 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -566,16 +566,17 @@ struct sctp_af {
int __user *optlen);
struct dst_entry *(*get_dst) (struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr);
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk);
void (*get_saddr) (struct sctp_sock *sk,
- struct sctp_association *asoc,
- struct dst_entry *dst,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr);
+ struct flowi *fl);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr,
- struct dst_entry *dst,
+ void *from,
__be16 port);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
@@ -1061,7 +1062,7 @@ void sctp_transport_set_owner(struct sctp_transport *,
struct sctp_association *);
void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
struct sctp_sock *);
-void sctp_transport_pmtu(struct sctp_transport *);
+void sctp_transport_pmtu(struct sctp_transport *, struct sock *sk);
void sctp_transport_free(struct sctp_transport *);
void sctp_transport_reset_timers(struct sctp_transport *);
void sctp_transport_hold(struct sctp_transport *);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 5adf585..cc9ea37 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -82,6 +82,10 @@
static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
union sctp_addr *s2);
+static void sctp_v6_dst_saddr(union sctp_addr *addr, void *from,
+ __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
@@ -245,69 +249,97 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport)
*/
static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk)
{
struct dst_entry *dst = NULL;
- struct flowi6 fl6;
+ struct flowi6 *fl6 = &fl->u.ip6;
struct sctp_bind_addr *bp;
struct sctp_sockaddr_entry *laddr;
union sctp_addr *baddr = NULL;
+ union sctp_addr dst_saddr;
__u8 matchlen = 0;
__u8 bmatchlen;
sctp_scope_t scope;
+ int err = 0;
- 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);
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 (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);
+ SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr);
}
- dst = ip6_route_output(&init_net, NULL, &fl6);
+ err = ip6_dst_lookup(sk, &dst, fl6);
if (!asoc || saddr)
goto out;
- if (dst->error) {
- dst_release(dst);
- dst = NULL;
- bp = &asoc->base.bind_addr;
- scope = sctp_scope(daddr);
- /* Walk through the bind address list and try to get a dst that
- * matches a bind address as the source address.
+ 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 (!err) {
+ /* Walk through the bind address list and look for a bind
+ * address that matches the source address of the returned dst.
*/
+ sctp_v6_dst_saddr(&dst_saddr, fl6, htons(bp->port));
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
- if (!laddr->valid)
+ if (!laddr->valid || (laddr->state != SCTP_ADDR_SRC))
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;
- }
+
+ /* 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();
- if (baddr) {
- ipv6_addr_copy(&fl6.saddr, &baddr->v6.sin6_addr);
- dst = ip6_route_output(&init_net, NULL, &fl6);
+ /* None of the bound addresses match the source address of the
+ * dst. So release it.
+ */
+ dst_release(dst);
+ dst = NULL;
+ }
+
+ /* 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)
+ 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;
+ }
}
}
+ rcu_read_unlock();
+ if (baddr) {
+ ipv6_addr_copy(&fl6->saddr, &baddr->v6.sin6_addr);
+ err = ip6_dst_lookup(sk, &dst, fl6);
+ }
+
out:
- if (!dst->error) {
+ if (dst) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n",
- &rt->rt6i_dst.addr, &rt->rt6i_src.addr);
+ &rt->rt6i_dst.addr, &fl6->saddr);
return dst;
}
SCTP_DEBUG_PRINTK("NO ROUTE\n");
@@ -329,64 +361,21 @@ 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,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ 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;
- }
+ __func__, t->asoc, t->dst, &daddr->v6.sin6_addr);
- scope = sctp_scope(daddr);
-
- bp = &asoc->base.bind_addr;
-
- /* 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. */
@@ -508,14 +497,16 @@ 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 a dst_entry. Problem is that v6 dst
+ * entries do not carry the source, so we pass a flowi instead.
+ */
+static void sctp_v6_dst_saddr(union sctp_addr *addr, void *from,
__be16 port)
{
- struct rt6_info *rt = (struct rt6_info *)dst;
+ struct flowi6 *fl6 = (struct flowi6 *)from;
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, &fl6->saddr);
}
/* Compare addresses exactly.
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index d5bf91d..7be5df6 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -339,10 +339,10 @@ 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, void *from,
__be16 port)
{
- struct rtable *rt = (struct rtable *)dst;
+ struct rtable *rt = (struct rtable *)from;
saddr->v4.sin_family = AF_INET;
saddr->v4.sin_port = port;
saddr->v4.sin_addr.s_addr = rt->rt_src;
@@ -465,33 +465,35 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
*/
static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ union sctp_addr *saddr,
+ struct flowi *fl,
+ struct sock *sk)
{
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 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;
@@ -533,9 +535,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;
@@ -559,19 +561,15 @@ out:
* 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,
+ struct sctp_transport *t,
union sctp_addr *daddr,
- union sctp_addr *saddr)
+ 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;
}
}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index f694ee1..33d9ee6 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2287,7 +2287,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) {
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index d3ae493..2544b9b 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -211,11 +211,15 @@ 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;
+ struct flowi fl;
- dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
+ dst = transport->af_specific->get_dst(transport->asoc,
+ &transport->ipaddr,
+ &transport->saddr,
+ &fl, sk);
if (dst) {
transport->pathmtu = dst_mtu(dst);
@@ -272,15 +276,16 @@ void sctp_transport_route(struct sctp_transport *transport,
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
struct dst_entry *dst;
+ struct flowi fl;
- dst = af->get_dst(asoc, daddr, saddr);
+ dst = af->get_dst(asoc, daddr, saddr, &fl, sctp_opt2sk(opt));
+ transport->dst = dst;
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, daddr, &fl);
- transport->dst = dst;
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
return;
}
--
1.6.5.2
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox