* Re: pull request: wireless 2012-07-27
From: David Miller @ 2012-07-27 21:26 UTC (permalink / raw)
To: linville; +Cc: linux-wireless, netdev, linux-kernel
In-Reply-To: <20120727155806.GB6110@tuxdriver.com>
From: "John W. Linville" <linville@tuxdriver.com>
Date: Fri, 27 Jul 2012 11:58:06 -0400
> These fixes are intended for the 3.6 stream.
>
> Hauke Mehrtens provides a pair of bcma fixes, one to fix a build
> regression on mips and another to correct a pair of missing iounmap
> calls.
>
> Thomas Huehn offers a mac80211_hwsim fix to avoid a possible
> use-after-free bug.
>
> Please let me know if there are problems!
Pulled, thanks John.
^ permalink raw reply
* Re: [PATCH] ipv4: fix TCP early demux
From: Eric Dumazet @ 2012-07-27 21:34 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20120727.134713.1437322148321890583.davem@davemloft.net>
On Fri, 2012-07-27 at 13:47 -0700, David Miller wrote:
> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Fri, 27 Jul 2012 18:23:40 +0200
>
> > From: Eric Dumazet <edumazet@google.com>
> >
> > commit 92101b3b2e317 (ipv4: Prepare for change of rt->rt_iif encoding.)
> > invalidated TCP early demux, because rx_dst_ifindex is not properly
> > initialized and checked.
> >
> > Also remove the use of inet_iif(skb) in favor or skb->skb_iif
> >
> > Signed-off-by: Eric Dumazet <edumazet@google.com>
>
> Applied.
Thanks David
IPv6 part is screwed because of the bogus dst_check(dst, 0)
(and missing code that was moved out from tcp_rcv_established() to
tcp_v4_do_rcv() : I was wondering if we could make it generic to move it
back to tcp_rcv_established()) :
if (sk->sk_rx_dst) {
struct dst_entry *dst = sk->sk_rx_dst;
if (dst->ops->check(dst, 0) == NULL) {
dst_release(dst);
sk->sk_rx_dst = NULL;
}
}
if (unlikely(sk->sk_rx_dst == NULL)) {
sk->sk_rx_dst = dst_clone(skb_dst(skb));
inet_sk(sk)->rx_dst_ifindex = inet_iif(skb);
}
IPv6 wants a cookie here, not 0
I wonder why cookie is not stored in dst, and must be stored outside of
it ?
We could then use :
if (sk->sk_rx_dst) {
struct dst_entry *dst = sk->sk_rx_dst;
if (dst->ops->check(dst) == NULL) {
dst_release(dst);
sk->sk_rx_dst = NULL;
}
}
^ permalink raw reply
* Re: [PATCH] ipv4: fix TCP early demux
From: David Miller @ 2012-07-27 23:00 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1343424873.2626.13112.camel@edumazet-glaptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 27 Jul 2012 23:34:33 +0200
> I wonder why cookie is not stored in dst, and must be stored outside
> of it ?
Because in ipv6, cloned routes are never invalidated on a route
insertion or deletion. We keep them around.
The fn_sernum tracks path reachability, rather than validation of
dsts.
So when we delete or insert a new ipv6 route, we update the serial
number of all the FIB nodes on the way down to the insert/delete
point.
If, afterwards, we can still reach a route cloned from one of those
entries successfully. It is still valid.
If we were to store the serial number on the dst, it would invalidate
the cloned route, which the ipv6 code is largely not designed for.
To be honest, the whole route validation scheme in ipv6 was
jackhammered into place. It was basically two years of Alexey
repairing the largely broken scheme that Pedro had put into place.
This is ~1997 legacy stuff.
It deserves a complete rewrite, but we are too busy with ipv4 at the
moment. And frankly if nobody other than myself was concerned enough
to delete the ipv4 routing cache (code people actually use) the
likelyhood of anyone embarking on a task of similar size for ipv6 is
basically zero.
^ permalink raw reply
* [PATCH v2] ppp: add 64 bit stats
From: Kevin Groeneveld @ 2012-07-28 3:38 UTC (permalink / raw)
To: netdev; +Cc: Kevin Groeneveld
Add 64 bit stats to ppp driver. The 64 bit stats include tx_bytes,
rx_bytes, tx_packets and rx_packets. Other stats are still 32 bit.
The 64 bit stats can be retrieved via the ndo_get_stats operation. The
SIOCGPPPSTATS ioctl is still 32 bit stats only.
Signed-off-by: Kevin Groeneveld <kgroeneveld@gmail.com>
---
v2: - do not use percpu variables for stats
- use ppp_recv_lock/ppp_xmit_lock when reading 64 bit stats
drivers/net/ppp/ppp_generic.c | 58 ++++++++++++++++++++++++++++++++++-------
1 file changed, 48 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 5c05572..eb3f5ce 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -94,6 +94,18 @@ struct ppp_file {
#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
/*
+ * Data structure to hold primary network stats for which
+ * we want to use 64 bit storage. Other network stats
+ * are stored in dev->stats of the ppp strucute.
+ */
+struct ppp_link_stats {
+ u64 rx_packets;
+ u64 tx_packets;
+ u64 rx_bytes;
+ u64 tx_bytes;
+};
+
+/*
* Data structure describing one ppp unit.
* A ppp unit corresponds to a ppp network interface device
* and represents a multilink bundle.
@@ -136,6 +148,7 @@ struct ppp {
unsigned pass_len, active_len;
#endif /* CONFIG_PPP_FILTER */
struct net *ppp_net; /* the net we belong to */
+ struct ppp_link_stats stats64; /* 64 bit network stats */
};
/*
@@ -1021,9 +1034,34 @@ ppp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return err;
}
+struct rtnl_link_stats64*
+ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
+{
+ struct ppp *ppp = netdev_priv(dev);
+
+ ppp_recv_lock(ppp);
+ stats64->rx_packets = ppp->stats64.rx_packets;
+ stats64->rx_bytes = ppp->stats64.rx_bytes;
+ ppp_recv_unlock(ppp);
+
+ ppp_xmit_lock(ppp);
+ stats64->tx_packets = ppp->stats64.tx_packets;
+ stats64->tx_bytes = ppp->stats64.tx_bytes;
+ ppp_xmit_unlock(ppp);
+
+ stats64->rx_errors = dev->stats.rx_errors;
+ stats64->tx_errors = dev->stats.tx_errors;
+ stats64->rx_dropped = dev->stats.rx_dropped;
+ stats64->tx_dropped = dev->stats.tx_dropped;
+ stats64->rx_length_errors = dev->stats.rx_length_errors;
+
+ return stats64;
+}
+
static const struct net_device_ops ppp_netdev_ops = {
- .ndo_start_xmit = ppp_start_xmit,
- .ndo_do_ioctl = ppp_net_ioctl,
+ .ndo_start_xmit = ppp_start_xmit,
+ .ndo_do_ioctl = ppp_net_ioctl,
+ .ndo_get_stats64 = ppp_get_stats64,
};
static void ppp_setup(struct net_device *dev)
@@ -1157,8 +1195,8 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb)
#endif /* CONFIG_PPP_FILTER */
}
- ++ppp->dev->stats.tx_packets;
- ppp->dev->stats.tx_bytes += skb->len - 2;
+ ++ppp->stats64.tx_packets;
+ ppp->stats64.tx_bytes += skb->len - 2;
switch (proto) {
case PPP_IP:
@@ -1745,8 +1783,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
break;
}
- ++ppp->dev->stats.rx_packets;
- ppp->dev->stats.rx_bytes += skb->len - 2;
+ ++ppp->stats64.rx_packets;
+ ppp->stats64.rx_bytes += skb->len - 2;
npi = proto_to_npindex(proto);
if (npi < 0) {
@@ -2570,12 +2608,12 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st)
struct slcompress *vj = ppp->vj;
memset(st, 0, sizeof(*st));
- st->p.ppp_ipackets = ppp->dev->stats.rx_packets;
+ st->p.ppp_ipackets = ppp->stats64.rx_packets;
st->p.ppp_ierrors = ppp->dev->stats.rx_errors;
- st->p.ppp_ibytes = ppp->dev->stats.rx_bytes;
- st->p.ppp_opackets = ppp->dev->stats.tx_packets;
+ st->p.ppp_ibytes = ppp->stats64.rx_bytes;
+ st->p.ppp_opackets = ppp->stats64.tx_packets;
st->p.ppp_oerrors = ppp->dev->stats.tx_errors;
- st->p.ppp_obytes = ppp->dev->stats.tx_bytes;
+ st->p.ppp_obytes = ppp->stats64.tx_bytes;
if (!vj)
return;
st->vj.vjs_packets = vj->sls_o_compressed + vj->sls_o_uncompressed;
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 00/16] Remove the ipv4 routing cache
From: David Miller @ 2012-07-28 4:15 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
In-Reply-To: <20120726.230246.219188476590178857.davem@davemloft.net>
From: David Miller <davem@davemloft.net>
Date: Thu, 26 Jul 2012 23:02:46 -0700 (PDT)
> Therefore one area of simplification would be to just return a pointer
> to the FIB nexthop, rather than the fib_info pointer and the nexthop
> index. We can get to the fib_info, if we need to, via the nh_parent
> pointer of the nexthop.
So I'm about to post an RFC set of patches which show this kind
of simplification. It gets fib_result down to two members:
u32 tclassid;
struct fib_nh *nh;
If I could get rid of that tclassid it would be really nice. But
that's hard because the tclassid is fetched from the fib_rule and
all of that lookup path is abstracted behind a common layer
that's shared between ipv4 and ipv6 so it's a bit of work changing
arg conventions.
These changes help, but only ever so slightly, in my testing.
^ permalink raw reply
* [PATCH 0/7] Deconstruct struct fib_result
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
This patch set tries to move towards reducing struct fib_result down
to it's absolute minimum.
The eventual idea is to make it so that fib_lookup() simply
returns a "struct fib_nh *" and pointer encoded errnos, instead
of writing into a complicated structure as the return value on
the stack as is done now.
Signed-off-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply
* [PATCH 1/7] ipv4: Provide fib_nh instead of fib_info in fib_result.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
The latter can be obtained via nh->nh_parent, and this makes
the fib_result->nh_sel member no longer necessary.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 43 +++++++++++-------------
net/ipv4/devinet.c | 2 +-
net/ipv4/fib_frontend.c | 28 +++++++++-------
net/ipv4/fib_lookup.h | 11 -------
net/ipv4/fib_semantics.c | 54 +++++++++++++++---------------
net/ipv4/fib_trie.c | 7 ++--
net/ipv4/route.c | 82 ++++++++++++++++++++++------------------------
7 files changed, 108 insertions(+), 119 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index e69c3a4..eb62a2f 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -123,14 +123,14 @@ struct fib_rule;
struct fib_table;
struct fib_result {
- unsigned char prefixlen;
- unsigned char nh_sel;
- unsigned char type;
- unsigned char scope;
- u32 tclassid;
- struct fib_info *fi;
- struct fib_table *table;
- struct list_head *fa_head;
+ unsigned char prefixlen;
+ unsigned char __pad;
+ unsigned char type;
+ unsigned char scope;
+ u32 tclassid;
+ struct fib_nh *nh;
+ struct fib_table *table;
+ struct list_head *fa_head;
};
struct fib_result_nl {
@@ -150,31 +150,28 @@ struct fib_result_nl {
#ifdef CONFIG_IP_ROUTE_MULTIPATH
-#define FIB_RES_NH(res) ((res).fi->fib_nh[(res).nh_sel])
-
#define FIB_TABLE_HASHSZ 2
#else /* CONFIG_IP_ROUTE_MULTIPATH */
-#define FIB_RES_NH(res) ((res).fi->fib_nh[0])
-
#define FIB_TABLE_HASHSZ 256
#endif /* CONFIG_IP_ROUTE_MULTIPATH */
extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh);
-#define FIB_RES_SADDR(net, res) \
- ((FIB_RES_NH(res).nh_saddr_genid == \
- atomic_read(&(net)->ipv4.dev_addr_genid)) ? \
- FIB_RES_NH(res).nh_saddr : \
- fib_info_update_nh_saddr((net), &FIB_RES_NH(res)))
-#define FIB_RES_GW(res) (FIB_RES_NH(res).nh_gw)
-#define FIB_RES_DEV(res) (FIB_RES_NH(res).nh_dev)
-#define FIB_RES_OIF(res) (FIB_RES_NH(res).nh_oif)
+static inline __be32 fib_res_prefsrc(struct net *net, struct fib_result *res)
+{
+ struct fib_nh *nh = res->nh;
+ struct fib_info *fi;
-#define FIB_RES_PREFSRC(net, res) ((res).fi->fib_prefsrc ? : \
- FIB_RES_SADDR(net, res))
+ fi = nh->nh_parent;
+ if (fi->fib_prefsrc)
+ return fi->fib_prefsrc;
+ if (nh->nh_saddr_genid == atomic_read(&net->ipv4.dev_addr_genid))
+ return nh->nh_saddr;
+ return fib_info_update_nh_saddr(net, nh);
+}
struct fib_table {
struct hlist_node tb_hlist;
@@ -302,7 +299,7 @@ static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
#ifdef CONFIG_IP_MULTIPLE_TABLES
u32 rtag;
#endif
- *itag = FIB_RES_NH(*res).nh_tclassid<<16;
+ *itag = res->nh->nh_tclassid<<16;
#ifdef CONFIG_IP_MULTIPLE_TABLES
rtag = res->tclassid;
if (*itag == 0)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 44bf82e..1baaa53 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -164,7 +164,7 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
if (local &&
!fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
res.type == RTN_LOCAL)
- result = FIB_RES_DEV(res);
+ result = res.nh->nh_dev;
}
if (result && devref)
dev_hold(result);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 8732cc7..d2e8dbe 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -174,7 +174,7 @@ static inline unsigned int __inet_dev_addr_type(struct net *net,
ret = RTN_UNICAST;
rcu_read_lock();
if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) {
- if (!dev || dev == res.fi->fib_dev)
+ if (!dev || dev == res.nh->nh_dev)
ret = res.type;
}
rcu_read_unlock();
@@ -225,7 +225,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
fl4.flowi4_scope = scope;
fl4.flowi4_mark = IN_DEV_SRC_VMARK(in_dev) ? skb->mark : 0;
if (!fib_lookup(net, &fl4, &res))
- return FIB_RES_PREFSRC(net, res);
+ return fib_res_prefsrc(net, &res);
} else {
scope = RT_SCOPE_LINK;
}
@@ -247,6 +247,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
{
int ret, no_addr, accept_local;
struct fib_result res;
+ struct fib_info *fi;
struct flowi4 fl4;
struct net *net;
bool dev_match;
@@ -273,21 +274,17 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
fib_combine_itag(itag, &res);
dev_match = false;
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
- for (ret = 0; ret < res.fi->fib_nhs; ret++) {
- struct fib_nh *nh = &res.fi->fib_nh[ret];
+ fi = res.nh->nh_parent;
+ for (ret = 0; ret < fi->fib_nhs; ret++) {
+ struct fib_nh *nh = &fi->fib_nh[ret];
if (nh->nh_dev == dev) {
dev_match = true;
break;
}
}
-#else
- if (FIB_RES_DEV(res) == dev)
- dev_match = true;
-#endif
if (dev_match) {
- ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
+ ret = res.nh->nh_scope >= RT_SCOPE_HOST;
return ret;
}
if (no_addr)
@@ -299,7 +296,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
ret = 0;
if (fib_lookup(net, &fl4, &res) == 0) {
if (res.type == RTN_UNICAST)
- ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
+ ret = res.nh->nh_scope >= RT_SCOPE_HOST;
}
return ret;
@@ -939,8 +936,15 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
frn->err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF);
if (!frn->err) {
+ struct fib_nh *nh = res.nh;
+ struct fib_info *fi;
+ int nhsel;
+
+ fi = nh->nh_parent;
+ nhsel = nh - &fi->fib_nh[0];
+
frn->prefixlen = res.prefixlen;
- frn->nh_sel = res.nh_sel;
+ frn->nh_sel = nhsel;
frn->type = res.type;
frn->scope = res.scope;
}
diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h
index af0f14a..5f110b6 100644
--- a/net/ipv4/fib_lookup.h
+++ b/net/ipv4/fib_lookup.h
@@ -36,17 +36,6 @@ extern void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
unsigned int nlm_flags);
extern struct fib_alias *fib_find_alias(struct list_head *fah,
u8 tos, u32 prio);
-extern int fib_detect_death(struct fib_info *fi, int order,
- struct fib_info **last_resort,
- int *last_idx, int dflt);
-
-static inline void fib_result_assign(struct fib_result *res,
- struct fib_info *fi)
-{
- /* we used to play games with refcounts, but we now use RCU */
- res->fi = fi;
-}
-
struct fib_prop {
int error;
u8 scope;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index da0cc2e..b830245 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -393,13 +393,14 @@ struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
return NULL;
}
-int fib_detect_death(struct fib_info *fi, int order,
- struct fib_info **last_resort, int *last_idx, int dflt)
+int fib_detect_death(struct fib_nh *nh, int order,
+ struct fib_nh **last_resort, int *last_idx,
+ int dflt)
{
struct neighbour *n;
int state = NUD_NONE;
- n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev);
+ n = neigh_lookup(&arp_tbl, &nh->nh_gw, nh->nh_dev);
if (n) {
state = n->nud_state;
neigh_release(n);
@@ -410,7 +411,7 @@ int fib_detect_death(struct fib_info *fi, int order,
return 0;
if ((state & NUD_VALID) ||
(*last_idx < 0 && order > dflt)) {
- *last_resort = fi;
+ *last_resort = nh;
*last_idx = order;
}
return 1;
@@ -612,8 +613,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
goto out;
nh->nh_scope = res.scope;
- nh->nh_oif = FIB_RES_OIF(res);
- nh->nh_dev = dev = FIB_RES_DEV(res);
+ nh->nh_oif = res.nh->nh_oif;
+ nh->nh_dev = dev = res.nh->nh_dev;
if (!dev)
goto out;
dev_hold(dev);
@@ -1130,54 +1131,55 @@ int fib_sync_down_dev(struct net_device *dev, int force)
/* Must be invoked inside of an RCU protected region. */
void fib_select_default(struct fib_result *res)
{
- struct fib_info *fi = NULL, *last_resort = NULL;
+ struct fib_nh *nh = NULL, *last_resort = NULL;
struct list_head *fa_head = res->fa_head;
struct fib_table *tb = res->table;
int order = -1, last_idx = -1;
struct fib_alias *fa;
list_for_each_entry_rcu(fa, fa_head, fa_list) {
- struct fib_info *next_fi = fa->fa_info;
+ struct fib_nh *next_nh = &fa->fa_info->fib_nh[0];
- if (next_fi->fib_scope != res->scope ||
+ if (next_nh->nh_parent->fib_scope != res->scope ||
fa->fa_type != RTN_UNICAST)
continue;
- if (next_fi->fib_priority > res->fi->fib_priority)
+ if (next_nh->nh_parent->fib_priority >
+ res->nh->nh_parent->fib_priority)
break;
- if (!next_fi->fib_nh[0].nh_gw ||
- next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK)
+
+ if (!next_nh->nh_gw || next_nh->nh_scope != RT_SCOPE_LINK)
continue;
fib_alias_accessed(fa);
- if (fi == NULL) {
- if (next_fi != res->fi)
+ if (nh == NULL) {
+ if (next_nh != res->nh)
break;
- } else if (!fib_detect_death(fi, order, &last_resort,
+ } else if (!fib_detect_death(nh, order, &last_resort,
&last_idx, tb->tb_default)) {
- fib_result_assign(res, fi);
+ res->nh = nh;
tb->tb_default = order;
goto out;
}
- fi = next_fi;
+ nh = next_nh;
order++;
}
- if (order <= 0 || fi == NULL) {
+ if (order <= 0 || nh == NULL) {
tb->tb_default = -1;
goto out;
}
- if (!fib_detect_death(fi, order, &last_resort, &last_idx,
- tb->tb_default)) {
- fib_result_assign(res, fi);
+ if (!fib_detect_death(nh, order, &last_resort, &last_idx,
+ tb->tb_default)) {
+ res->nh = nh;
tb->tb_default = order;
goto out;
}
if (last_idx >= 0)
- fib_result_assign(res, last_resort);
+ res->nh = last_resort;
tb->tb_default = last_idx;
out:
return;
@@ -1249,7 +1251,7 @@ int fib_sync_up(struct net_device *dev)
*/
void fib_select_multipath(struct fib_result *res)
{
- struct fib_info *fi = res->fi;
+ struct fib_info *fi = res->nh->nh_parent;
int w;
spin_lock_bh(&fib_multipath_lock);
@@ -1265,7 +1267,7 @@ void fib_select_multipath(struct fib_result *res)
if (power <= 0) {
spin_unlock_bh(&fib_multipath_lock);
/* Race condition: route has just become dead. */
- res->nh_sel = 0;
+ res->nh = &fi->fib_nh[0];
return;
}
}
@@ -1284,7 +1286,7 @@ void fib_select_multipath(struct fib_result *res)
if (w <= 0) {
nexthop_nh->nh_power--;
fi->fib_power--;
- res->nh_sel = nhsel;
+ res->nh = &fi->fib_nh[nhsel];
spin_unlock_bh(&fib_multipath_lock);
return;
}
@@ -1292,7 +1294,7 @@ void fib_select_multipath(struct fib_result *res)
} endfor_nexthops(fi);
/* Race condition: route has just become dead. */
- res->nh_sel = 0;
+ res->nh = &fi->fib_nh[0];
spin_unlock_bh(&fib_multipath_lock);
}
#endif
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 18cbc15..04b0e26 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1384,7 +1384,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
if (fi->fib_flags & RTNH_F_DEAD)
continue;
for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) {
- const struct fib_nh *nh = &fi->fib_nh[nhsel];
+ struct fib_nh *nh = &fi->fib_nh[nhsel];
if (nh->nh_flags & RTNH_F_DEAD)
continue;
@@ -1395,10 +1395,9 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
t->stats.semantic_match_passed++;
#endif
res->prefixlen = li->plen;
- res->nh_sel = nhsel;
res->type = fa->fa_type;
- res->scope = fa->fa_info->fib_scope;
- res->fi = fi;
+ res->scope = fi->fib_scope;
+ res->nh = nh;
res->table = tb;
res->fa_head = &li->falh;
if (!(fib_flags & FIB_LOOKUP_NOREF))
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index fc1a81c..0b3277c 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -722,7 +722,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
neigh_event_send(n, NULL);
} else {
if (fib_lookup(net, fl4, &res) == 0) {
- struct fib_nh *nh = &FIB_RES_NH(res);
+ struct fib_nh *nh = res.nh;
update_or_create_fnhe(nh, fl4->daddr, new_gw,
0, 0);
@@ -926,7 +926,7 @@ static u32 __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
mtu = ip_rt_min_pmtu;
if (fib_lookup(dev_net(rt->dst.dev), fl4, &res) == 0) {
- struct fib_nh *nh = &FIB_RES_NH(res);
+ struct fib_nh *nh = res.nh;
update_or_create_fnhe(nh, fl4->daddr, 0, mtu,
jiffies + ip_rt_mtu_expires);
@@ -1085,7 +1085,7 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt)
rcu_read_lock();
if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res) == 0)
- src = FIB_RES_PREFSRC(dev_net(rt->dst.dev), res);
+ src = fib_res_prefsrc(dev_net(rt->dst.dev), &res);
else
src = inet_select_addr(rt->dst.dev,
rt_nexthop(rt, iph->daddr),
@@ -1237,16 +1237,14 @@ static bool rt_cache_valid(const struct rtable *rt)
static void rt_set_nexthop(struct rtable *rt, __be32 daddr,
const struct fib_result *res,
struct fib_nh_exception *fnhe,
- struct fib_info *fi, u16 type, u32 itag)
+ struct fib_nh *nh, u16 type, u32 itag)
{
- if (fi) {
- struct fib_nh *nh = &FIB_RES_NH(*res);
-
+ if (nh) {
if (nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK)
rt->rt_gateway = nh->nh_gw;
if (unlikely(fnhe))
rt_bind_exception(rt, fnhe, daddr);
- dst_init_metrics(&rt->dst, fi->fib_metrics, true);
+ dst_init_metrics(&rt->dst, nh->nh_parent->fib_metrics, true);
#ifdef CONFIG_IP_ROUTE_CLASSID
rt->dst.tclassid = nh->nh_tclassid;
#endif
@@ -1373,6 +1371,7 @@ static int __mkroute_input(struct sk_buff *skb,
struct in_device *in_dev,
__be32 daddr, __be32 saddr, u32 tos)
{
+ struct fib_nh *nh = res->nh;
struct rtable *rth;
int err;
struct in_device *out_dev;
@@ -1381,14 +1380,14 @@ static int __mkroute_input(struct sk_buff *skb,
u32 itag;
/* get a working reference to the output device */
- out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res));
+ out_dev = __in_dev_get_rcu(nh->nh_dev);
if (out_dev == NULL) {
net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");
return -EINVAL;
}
- err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res),
+ err = fib_validate_source(skb, saddr, daddr, tos, nh->nh_oif,
in_dev->dev, in_dev, &itag);
if (err < 0) {
ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
@@ -1399,7 +1398,7 @@ static int __mkroute_input(struct sk_buff *skb,
if (out_dev == in_dev && err &&
(IN_DEV_SHARED_MEDIA(out_dev) ||
- inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res))))
+ inet_addr_onlink(out_dev, saddr, nh->nh_gw)))
flags |= RTCF_DOREDIRECT;
if (skb->protocol != htons(ETH_P_IP)) {
@@ -1418,15 +1417,13 @@ static int __mkroute_input(struct sk_buff *skb,
}
do_cache = false;
- if (res->fi) {
- if (!itag) {
- rth = FIB_RES_NH(*res).nh_rth_input;
- if (rt_cache_valid(rth)) {
- skb_dst_set_noref(skb, &rth->dst);
- goto out;
- }
- do_cache = true;
+ if (!itag) {
+ rth = nh->nh_rth_input;
+ if (rt_cache_valid(rth)) {
+ skb_dst_set_noref(skb, &rth->dst);
+ goto out;
}
+ do_cache = true;
}
rth = rt_dst_alloc(out_dev->dev,
@@ -1448,7 +1445,7 @@ static int __mkroute_input(struct sk_buff *skb,
rth->dst.input = ip_forward;
rth->dst.output = ip_output;
- rt_set_nexthop(rth, daddr, res, NULL, res->fi, res->type, itag);
+ rt_set_nexthop(rth, daddr, res, NULL, nh, res->type, itag);
skb_dst_set(skb, &rth->dst);
out:
err = 0;
@@ -1463,7 +1460,7 @@ static int ip_mkroute_input(struct sk_buff *skb,
__be32 daddr, __be32 saddr, u32 tos)
{
#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res->fi && res->fi->fib_nhs > 1)
+ if (res->nh->nh_parent->fib_nhs > 1)
fib_select_multipath(res);
#endif
@@ -1507,7 +1504,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
if (ipv4_is_multicast(saddr) || ipv4_is_lbcast(saddr))
goto martian_source;
- res.fi = NULL;
+ res.nh = NULL;
if (ipv4_is_lbcast(daddr) || (saddr == 0 && daddr == 0))
goto brd_input;
@@ -1580,9 +1577,9 @@ brd_input:
local_input:
do_cache = false;
- if (res.fi) {
+ if (res.nh) {
if (!itag) {
- rth = FIB_RES_NH(res).nh_rth_input;
+ rth = res.nh->nh_rth_input;
if (rt_cache_valid(rth)) {
skb_dst_set_noref(skb, &rth->dst);
err = 0;
@@ -1616,7 +1613,7 @@ local_input:
rth->rt_flags &= ~RTCF_LOCAL;
}
if (do_cache)
- rt_cache_route(&FIB_RES_NH(res), rth);
+ rt_cache_route(res.nh, rth);
skb_dst_set(skb, &rth->dst);
err = 0;
goto out;
@@ -1706,8 +1703,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
struct net_device *dev_out,
unsigned int flags)
{
- struct fib_info *fi = res->fi;
struct fib_nh_exception *fnhe;
+ struct fib_nh *nh = res->nh;
struct in_device *in_dev;
u16 type = res->type;
struct rtable *rth;
@@ -1732,7 +1729,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
if (type == RTN_BROADCAST) {
flags |= RTCF_BROADCAST | RTCF_LOCAL;
- fi = NULL;
+ nh = NULL;
} else if (type == RTN_MULTICAST) {
flags |= RTCF_MULTICAST | RTCF_LOCAL;
if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr,
@@ -1742,15 +1739,15 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
* default one, but do not gateway in this case.
* Yes, it is hack.
*/
- if (fi && res->prefixlen < 4)
- fi = NULL;
+ if (nh && res->prefixlen < 4)
+ nh = NULL;
}
fnhe = NULL;
- if (fi) {
- fnhe = find_exception(&FIB_RES_NH(*res), fl4->daddr);
+ if (nh) {
+ fnhe = find_exception(nh, fl4->daddr);
if (!fnhe) {
- rth = FIB_RES_NH(*res).nh_rth_output;
+ rth = nh->nh_rth_output;
if (rt_cache_valid(rth)) {
dst_hold(&rth->dst);
return rth;
@@ -1760,7 +1757,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
rth = rt_dst_alloc(dev_out,
IN_DEV_CONF_GET(in_dev, NOPOLICY),
IN_DEV_CONF_GET(in_dev, NOXFRM),
- fi && !fnhe);
+ nh && !fnhe);
if (!rth)
return ERR_PTR(-ENOBUFS);
@@ -1795,7 +1792,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
#endif
}
- rt_set_nexthop(rth, fl4->daddr, res, fnhe, fi, type, 0);
+ rt_set_nexthop(rth, fl4->daddr, res, fnhe, nh, type, 0);
return rth;
}
@@ -1814,7 +1811,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
int orig_oif;
res.tclassid = 0;
- res.fi = NULL;
+ res.nh = NULL;
res.table = NULL;
orig_oif = fl4->flowi4_oif;
@@ -1915,7 +1912,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
}
if (fib_lookup(net, fl4, &res)) {
- res.fi = NULL;
+ res.nh = NULL;
res.table = NULL;
if (fl4->flowi4_oif) {
/* Apparently, routing tables are wrong. Assume,
@@ -1948,20 +1945,21 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
if (res.type == RTN_LOCAL) {
if (!fl4->saddr) {
- if (res.fi->fib_prefsrc)
- fl4->saddr = res.fi->fib_prefsrc;
+ struct fib_info *fi = res.nh->nh_parent;
+ if (fi->fib_prefsrc)
+ fl4->saddr = fi->fib_prefsrc;
else
fl4->saddr = fl4->daddr;
}
dev_out = net->loopback_dev;
fl4->flowi4_oif = dev_out->ifindex;
- res.fi = NULL;
+ res.nh = NULL;
flags |= RTCF_LOCAL;
goto make_route;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res.fi->fib_nhs > 1 && fl4->flowi4_oif == 0)
+ if (res.nh->nh_parent->fib_nhs > 1 && fl4->flowi4_oif == 0)
fib_select_multipath(&res);
else
#endif
@@ -1971,9 +1969,9 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
fib_select_default(&res);
if (!fl4->saddr)
- fl4->saddr = FIB_RES_PREFSRC(net, res);
+ fl4->saddr = fib_res_prefsrc(net, &res);
- dev_out = FIB_RES_DEV(res);
+ dev_out = res.nh->nh_dev;
fl4->flowi4_oif = dev_out->ifindex;
--
1.7.10.4
^ permalink raw reply related
* [PATCH 2/7] ipv4: Don't store scope in fib_result.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
It can be obtained from the nexthop's fib_info.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 2 +-
net/ipv4/fib_frontend.c | 2 +-
net/ipv4/fib_semantics.c | 5 +++--
net/ipv4/fib_trie.c | 1 -
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index eb62a2f..847a46b 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -126,7 +126,7 @@ struct fib_result {
unsigned char prefixlen;
unsigned char __pad;
unsigned char type;
- unsigned char scope;
+ unsigned char __pad2;
u32 tclassid;
struct fib_nh *nh;
struct fib_table *table;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index d2e8dbe..e02390d 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -946,7 +946,7 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
frn->prefixlen = res.prefixlen;
frn->nh_sel = nhsel;
frn->type = res.type;
- frn->scope = res.scope;
+ frn->scope = fi->fib_scope;
}
rcu_read_unlock();
local_bh_enable();
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index b830245..6f73403 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -612,7 +612,7 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
err = -EINVAL;
if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
goto out;
- nh->nh_scope = res.scope;
+ nh->nh_scope = res.nh->nh_parent->fib_scope;
nh->nh_oif = res.nh->nh_oif;
nh->nh_dev = dev = res.nh->nh_dev;
if (!dev)
@@ -1140,7 +1140,8 @@ void fib_select_default(struct fib_result *res)
list_for_each_entry_rcu(fa, fa_head, fa_list) {
struct fib_nh *next_nh = &fa->fa_info->fib_nh[0];
- if (next_nh->nh_parent->fib_scope != res->scope ||
+ if ((next_nh->nh_parent->fib_scope !=
+ res->nh->nh_parent->fib_scope) ||
fa->fa_type != RTN_UNICAST)
continue;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 04b0e26..83cf215 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1396,7 +1396,6 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
#endif
res->prefixlen = li->plen;
res->type = fa->fa_type;
- res->scope = fi->fib_scope;
res->nh = nh;
res->table = tb;
res->fa_head = &li->falh;
--
1.7.10.4
^ permalink raw reply related
* [PATCH 3/7] ipv4: Store route type in fib_info.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
This removes another value recorded in the fib_result return struct.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 3 ++-
net/ipv4/devinet.c | 2 +-
net/ipv4/fib_frontend.c | 10 +++++-----
net/ipv4/fib_lookup.h | 6 ++----
net/ipv4/fib_semantics.c | 17 ++++++++++-------
net/ipv4/fib_trie.c | 20 ++++++++------------
net/ipv4/route.c | 36 ++++++++++++++++++++----------------
7 files changed, 48 insertions(+), 46 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 847a46b..3cb9e45 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -100,6 +100,7 @@ struct fib_info {
unsigned char fib_dead;
unsigned char fib_protocol;
unsigned char fib_scope;
+ unsigned char fib_type;
__be32 fib_prefsrc;
u32 fib_priority;
u32 *fib_metrics;
@@ -125,8 +126,8 @@ struct fib_table;
struct fib_result {
unsigned char prefixlen;
unsigned char __pad;
- unsigned char type;
unsigned char __pad2;
+ unsigned char __pad3;
u32 tclassid;
struct fib_nh *nh;
struct fib_table *table;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 1baaa53..1595a67d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -163,7 +163,7 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
local = fib_get_table(net, RT_TABLE_LOCAL);
if (local &&
!fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
- res.type == RTN_LOCAL)
+ res.nh->nh_parent->fib_type == RTN_LOCAL)
result = res.nh->nh_dev;
}
if (result && devref)
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index e02390d..495b540 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -175,7 +175,7 @@ static inline unsigned int __inet_dev_addr_type(struct net *net,
rcu_read_lock();
if (!fib_table_lookup(local_table, &fl4, &res, FIB_LOOKUP_NOREF)) {
if (!dev || dev == res.nh->nh_dev)
- ret = res.type;
+ ret = res.nh->nh_parent->fib_type;
}
rcu_read_unlock();
}
@@ -267,8 +267,8 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
net = dev_net(dev);
if (fib_lookup(net, &fl4, &res))
goto last_resort;
- if (res.type != RTN_UNICAST) {
- if (res.type != RTN_LOCAL || !accept_local)
+ if (res.nh->nh_parent->fib_type != RTN_UNICAST) {
+ if (res.nh->nh_parent->fib_type != RTN_LOCAL || !accept_local)
goto e_inval;
}
fib_combine_itag(itag, &res);
@@ -295,7 +295,7 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
ret = 0;
if (fib_lookup(net, &fl4, &res) == 0) {
- if (res.type == RTN_UNICAST)
+ if (res.nh->nh_parent->fib_type == RTN_UNICAST)
ret = res.nh->nh_scope >= RT_SCOPE_HOST;
}
return ret;
@@ -945,7 +945,7 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
frn->prefixlen = res.prefixlen;
frn->nh_sel = nhsel;
- frn->type = res.type;
+ frn->type = fi->fib_type;
frn->scope = fi->fib_scope;
}
rcu_read_unlock();
diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h
index 5f110b6..3dd6b49 100644
--- a/net/ipv4/fib_lookup.h
+++ b/net/ipv4/fib_lookup.h
@@ -9,7 +9,6 @@ struct fib_alias {
struct list_head fa_list;
struct fib_info *fa_info;
u8 fa_tos;
- u8 fa_type;
u8 fa_state;
struct rcu_head rcu;
};
@@ -28,9 +27,8 @@ extern void fib_release_info(struct fib_info *);
extern struct fib_info *fib_create_info(struct fib_config *cfg);
extern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi);
extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
- u32 tb_id, u8 type, __be32 dst,
- int dst_len, u8 tos, struct fib_info *fi,
- unsigned int);
+ u32 tb_id, __be32 dst, int dst_len, u8 tos,
+ struct fib_info *fi, unsigned int);
extern void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
int dst_len, u32 tb_id, struct nl_info *info,
unsigned int nlm_flags);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 6f73403..13b801a 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -252,7 +252,8 @@ static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
unsigned int mask = (fib_info_hash_size - 1);
unsigned int val = fi->fib_nhs;
- val ^= (fi->fib_protocol << 8) | fi->fib_scope;
+ val ^= ((fi->fib_protocol << 16) | (fi->fib_scope << 8) |
+ fi->fib_type);
val ^= (__force u32)fi->fib_prefsrc;
val ^= fi->fib_priority;
for_nexthops(fi) {
@@ -279,6 +280,7 @@ static struct fib_info *fib_find_info(const struct fib_info *nfi)
continue;
if (nfi->fib_protocol == fi->fib_protocol &&
nfi->fib_scope == fi->fib_scope &&
+ nfi->fib_type == fi->fib_type &&
nfi->fib_prefsrc == fi->fib_prefsrc &&
nfi->fib_priority == fi->fib_priority &&
memcmp(nfi->fib_metrics, fi->fib_metrics,
@@ -359,8 +361,7 @@ void rtmsg_fib(int event, __be32 key, struct fib_alias *fa,
goto errout;
err = fib_dump_info(skb, info->pid, seq, event, tb_id,
- fa->fa_type, key, dst_len,
- fa->fa_tos, fa->fa_info, nlm_flags);
+ key, dst_len, fa->fa_tos, fa->fa_info, nlm_flags);
if (err < 0) {
/* -EMSGSIZE implies BUG in fib_nlmsg_size() */
WARN_ON(err == -EMSGSIZE);
@@ -610,7 +611,8 @@ static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi,
}
}
err = -EINVAL;
- if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
+ if (res.nh->nh_parent->fib_type != RTN_UNICAST &&
+ res.nh->nh_parent->fib_type != RTN_LOCAL)
goto out;
nh->nh_scope = res.nh->nh_parent->fib_scope;
nh->nh_oif = res.nh->nh_oif;
@@ -798,6 +800,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
fi->fib_net = hold_net(net);
fi->fib_protocol = cfg->fc_protocol;
fi->fib_scope = cfg->fc_scope;
+ fi->fib_type = cfg->fc_type;
fi->fib_flags = cfg->fc_flags;
fi->fib_priority = cfg->fc_priority;
fi->fib_prefsrc = cfg->fc_prefsrc;
@@ -957,7 +960,7 @@ failure:
}
int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
- u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos,
+ u32 tb_id, __be32 dst, int dst_len, u8 tos,
struct fib_info *fi, unsigned int flags)
{
struct nlmsghdr *nlh;
@@ -978,7 +981,7 @@ int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
rtm->rtm_table = RT_TABLE_COMPAT;
if (nla_put_u32(skb, RTA_TABLE, tb_id))
goto nla_put_failure;
- rtm->rtm_type = type;
+ rtm->rtm_type = fi->fib_type;
rtm->rtm_flags = fi->fib_flags;
rtm->rtm_scope = fi->fib_scope;
rtm->rtm_protocol = fi->fib_protocol;
@@ -1142,7 +1145,7 @@ void fib_select_default(struct fib_result *res)
if ((next_nh->nh_parent->fib_scope !=
res->nh->nh_parent->fib_scope) ||
- fa->fa_type != RTN_UNICAST)
+ next_nh->nh_parent->fib_type != RTN_UNICAST)
continue;
if (next_nh->nh_parent->fib_priority >
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 83cf215..f812f06 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1253,8 +1253,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
break;
if (fa->fa_info->fib_priority != fi->fib_priority)
break;
- if (fa->fa_type == cfg->fc_type &&
- fa->fa_info == fi) {
+ if (fa->fa_info == fi) {
fa_match = fa;
break;
}
@@ -1278,7 +1277,6 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
fi_drop = fa->fa_info;
new_fa->fa_tos = fa->fa_tos;
new_fa->fa_info = fi;
- new_fa->fa_type = cfg->fc_type;
state = fa->fa_state;
new_fa->fa_state = state & ~FA_S_ACCESSED;
@@ -1314,7 +1312,6 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
new_fa->fa_info = fi;
new_fa->fa_tos = tos;
- new_fa->fa_type = cfg->fc_type;
new_fa->fa_state = 0;
/*
* Insert new entry to the list.
@@ -1374,7 +1371,7 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
if (fa->fa_info->fib_scope < flp->flowi4_scope)
continue;
fib_alias_accessed(fa);
- err = fib_props[fa->fa_type].error;
+ err = fib_props[fi->fib_type].error;
if (err) {
#ifdef CONFIG_IP_FIB_TRIE_STATS
t->stats.semantic_match_passed++;
@@ -1395,7 +1392,6 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
t->stats.semantic_match_passed++;
#endif
res->prefixlen = li->plen;
- res->type = fa->fa_type;
res->nh = nh;
res->table = tb;
res->fa_head = &li->falh;
@@ -1670,7 +1666,8 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
if (fa->fa_tos != tos)
break;
- if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) &&
+ if ((!cfg->fc_type ||
+ fa->fa_info->fib_type == cfg->fc_type) &&
(cfg->fc_scope == RT_SCOPE_NOWHERE ||
fa->fa_info->fib_scope == cfg->fc_scope) &&
(!cfg->fc_prefsrc ||
@@ -1873,7 +1870,6 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
cb->nlh->nlmsg_seq,
RTM_NEWROUTE,
tb->tb_id,
- fa->fa_type,
xkey,
plen,
fa->fa_tos,
@@ -2394,7 +2390,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
rtn_scope(buf1, sizeof(buf1),
fa->fa_info->fib_scope),
rtn_type(buf2, sizeof(buf2),
- fa->fa_type));
+ fa->fa_info->fib_type));
if (fa->fa_tos)
seq_printf(seq, " tos=%d", fa->fa_tos);
seq_putc(seq, '\n');
@@ -2546,11 +2542,11 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
list_for_each_entry_rcu(fa, &li->falh, fa_list) {
const struct fib_info *fi = fa->fa_info;
- unsigned int flags = fib_flag_trans(fa->fa_type, mask, fi);
+ unsigned int flags = fib_flag_trans(fi->fib_type, mask, fi);
int len;
- if (fa->fa_type == RTN_BROADCAST
- || fa->fa_type == RTN_MULTICAST)
+ if (fi->fib_type == RTN_BROADCAST ||
+ fi->fib_type == RTN_MULTICAST)
continue;
if (fi)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 0b3277c..6d4353b 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1436,7 +1436,7 @@ static int __mkroute_input(struct sk_buff *skb,
rth->rt_genid = rt_genid(dev_net(rth->dst.dev));
rth->rt_flags = flags;
- rth->rt_type = res->type;
+ rth->rt_type = res->nh->nh_parent->fib_type;
rth->rt_is_input = 1;
rth->rt_iif = 0;
rth->rt_pmtu = 0;
@@ -1445,7 +1445,7 @@ static int __mkroute_input(struct sk_buff *skb,
rth->dst.input = ip_forward;
rth->dst.output = ip_output;
- rt_set_nexthop(rth, daddr, res, NULL, nh, res->type, itag);
+ rt_set_nexthop(rth, daddr, res, NULL, nh, rth->rt_type, itag);
skb_dst_set(skb, &rth->dst);
out:
err = 0;
@@ -1491,6 +1491,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
int err = -EINVAL;
struct net *net = dev_net(dev);
bool do_cache;
+ u8 type;
/* IP on this device is disabled. */
@@ -1541,10 +1542,11 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
RT_CACHE_STAT_INC(in_slow_tot);
- if (res.type == RTN_BROADCAST)
+ type = res.nh->nh_parent->fib_type;
+ if (type == RTN_BROADCAST)
goto brd_input;
- if (res.type == RTN_LOCAL) {
+ if (type == RTN_LOCAL) {
err = fib_validate_source(skb, saddr, daddr, tos,
net->loopback_dev->ifindex,
dev, in_dev, &itag);
@@ -1555,7 +1557,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
if (!IN_DEV_FORWARD(in_dev))
goto no_route;
- if (res.type != RTN_UNICAST)
+ if (type != RTN_UNICAST)
goto martian_destination;
err = ip_mkroute_input(skb, &res, &fl4, in_dev, daddr, saddr, tos);
@@ -1572,7 +1574,7 @@ brd_input:
goto martian_source_keep_err;
}
flags |= RTCF_BROADCAST;
- res.type = RTN_BROADCAST;
+ type = RTN_BROADCAST;
RT_CACHE_STAT_INC(in_brd);
local_input:
@@ -1602,12 +1604,12 @@ local_input:
rth->rt_genid = rt_genid(net);
rth->rt_flags = flags|RTCF_LOCAL;
- rth->rt_type = res.type;
+ rth->rt_type = type;
rth->rt_is_input = 1;
rth->rt_iif = 0;
rth->rt_pmtu = 0;
rth->rt_gateway = 0;
- if (res.type == RTN_UNREACHABLE) {
+ if (type == RTN_UNREACHABLE) {
rth->dst.input= ip_error;
rth->dst.error= -err;
rth->rt_flags &= ~RTCF_LOCAL;
@@ -1620,7 +1622,7 @@ local_input:
no_route:
RT_CACHE_STAT_INC(in_no_route);
- res.type = RTN_UNREACHABLE;
+ type = RTN_UNREACHABLE;
if (err == -ESRCH)
err = -ENETUNREACH;
goto local_input;
@@ -1700,13 +1702,12 @@ EXPORT_SYMBOL(ip_route_input_noref);
/* called with rcu_read_lock() */
static struct rtable *__mkroute_output(const struct fib_result *res,
const struct flowi4 *fl4, int orig_oif,
- struct net_device *dev_out,
+ struct net_device *dev_out, u8 type,
unsigned int flags)
{
struct fib_nh_exception *fnhe;
struct fib_nh *nh = res->nh;
struct in_device *in_dev;
- u16 type = res->type;
struct rtable *rth;
in_dev = __in_dev_get_rcu(dev_out);
@@ -1809,6 +1810,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
struct fib_result res;
struct rtable *rth;
int orig_oif;
+ u8 type;
res.tclassid = 0;
res.nh = NULL;
@@ -1822,6 +1824,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
RT_SCOPE_LINK : RT_SCOPE_UNIVERSE);
rcu_read_lock();
+ type = 0;
if (fl4->saddr) {
rth = ERR_PTR(-EINVAL);
if (ipv4_is_multicast(fl4->saddr) ||
@@ -1906,7 +1909,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
fl4->daddr = fl4->saddr = htonl(INADDR_LOOPBACK);
dev_out = net->loopback_dev;
fl4->flowi4_oif = net->loopback_dev->ifindex;
- res.type = RTN_LOCAL;
+ type = RTN_LOCAL;
flags |= RTCF_LOCAL;
goto make_route;
}
@@ -1936,14 +1939,15 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
if (fl4->saddr == 0)
fl4->saddr = inet_select_addr(dev_out, 0,
RT_SCOPE_LINK);
- res.type = RTN_UNICAST;
+ type = RTN_UNICAST;
goto make_route;
}
rth = ERR_PTR(-ENETUNREACH);
goto out;
}
+ type = res.nh->nh_parent->fib_type;
- if (res.type == RTN_LOCAL) {
+ if (type == RTN_LOCAL) {
if (!fl4->saddr) {
struct fib_info *fi = res.nh->nh_parent;
if (fi->fib_prefsrc)
@@ -1965,7 +1969,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
#endif
if (!res.prefixlen &&
res.table->tb_num_default > 1 &&
- res.type == RTN_UNICAST && !fl4->flowi4_oif)
+ type == RTN_UNICAST && !fl4->flowi4_oif)
fib_select_default(&res);
if (!fl4->saddr)
@@ -1976,7 +1980,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
make_route:
- rth = __mkroute_output(&res, fl4, orig_oif, dev_out, flags);
+ rth = __mkroute_output(&res, fl4, orig_oif, dev_out, type, flags);
out:
rcu_read_unlock();
--
1.7.10.4
^ permalink raw reply related
* [PATCH 4/7] ipv4: Store prefixlen in fib_info.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
The shrinks fib_result by 4 bytes since we don't have to write
it there any longer.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 8 +++-----
net/ipv4/fib_frontend.c | 2 +-
net/ipv4/fib_semantics.c | 6 ++++--
net/ipv4/fib_trie.c | 1 -
net/ipv4/route.c | 4 ++--
5 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 3cb9e45..43be7230 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -96,11 +96,13 @@ struct fib_info {
struct net *fib_net;
int fib_treeref;
atomic_t fib_clntref;
- unsigned int fib_flags;
+ unsigned char fib_flags;
unsigned char fib_dead;
unsigned char fib_protocol;
unsigned char fib_scope;
unsigned char fib_type;
+ unsigned char fib_prefixlen;
+ unsigned short __pad;
__be32 fib_prefsrc;
u32 fib_priority;
u32 *fib_metrics;
@@ -124,10 +126,6 @@ struct fib_rule;
struct fib_table;
struct fib_result {
- unsigned char prefixlen;
- unsigned char __pad;
- unsigned char __pad2;
- unsigned char __pad3;
u32 tclassid;
struct fib_nh *nh;
struct fib_table *table;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 495b540..1e1907c 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -943,7 +943,7 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb)
fi = nh->nh_parent;
nhsel = nh - &fi->fib_nh[0];
- frn->prefixlen = res.prefixlen;
+ frn->prefixlen = fi->fib_prefixlen;
frn->nh_sel = nhsel;
frn->type = fi->fib_type;
frn->scope = fi->fib_scope;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 13b801a..bf4c809 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -252,8 +252,8 @@ static inline unsigned int fib_info_hashfn(const struct fib_info *fi)
unsigned int mask = (fib_info_hash_size - 1);
unsigned int val = fi->fib_nhs;
- val ^= ((fi->fib_protocol << 16) | (fi->fib_scope << 8) |
- fi->fib_type);
+ val ^= ((fi->fib_protocol << 24) | (fi->fib_scope << 16) |
+ (fi->fib_type << 8) | fi->fib_prefixlen);
val ^= (__force u32)fi->fib_prefsrc;
val ^= fi->fib_priority;
for_nexthops(fi) {
@@ -281,6 +281,7 @@ static struct fib_info *fib_find_info(const struct fib_info *nfi)
if (nfi->fib_protocol == fi->fib_protocol &&
nfi->fib_scope == fi->fib_scope &&
nfi->fib_type == fi->fib_type &&
+ nfi->fib_prefixlen == fi->fib_prefixlen &&
nfi->fib_prefsrc == fi->fib_prefsrc &&
nfi->fib_priority == fi->fib_priority &&
memcmp(nfi->fib_metrics, fi->fib_metrics,
@@ -801,6 +802,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
fi->fib_protocol = cfg->fc_protocol;
fi->fib_scope = cfg->fc_scope;
fi->fib_type = cfg->fc_type;
+ fi->fib_prefixlen = cfg->fc_dst_len;
fi->fib_flags = cfg->fc_flags;
fi->fib_priority = cfg->fc_priority;
fi->fib_prefsrc = cfg->fc_prefsrc;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index f812f06..3d1d450 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1391,7 +1391,6 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
#ifdef CONFIG_IP_FIB_TRIE_STATS
t->stats.semantic_match_passed++;
#endif
- res->prefixlen = li->plen;
res->nh = nh;
res->table = tb;
res->fa_head = &li->falh;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6d4353b..597fbc0 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1740,7 +1740,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
* default one, but do not gateway in this case.
* Yes, it is hack.
*/
- if (nh && res->prefixlen < 4)
+ if (nh && nh->nh_parent->fib_prefixlen < 4)
nh = NULL;
}
@@ -1967,7 +1967,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
fib_select_multipath(&res);
else
#endif
- if (!res.prefixlen &&
+ if (!res.nh->nh_parent->fib_prefixlen &&
res.table->tb_num_default > 1 &&
type == RTN_UNICAST && !fl4->flowi4_oif)
fib_select_default(&res);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 5/7] ipv4: Make fib_select_default() not depend upon fib_result.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
Pass the arguments and return the result explicitly.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 3 ++-
net/ipv4/fib_semantics.c | 19 +++++++++----------
net/ipv4/route.c | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 43be7230..8a57513 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -267,7 +267,8 @@ extern __be32 fib_compute_spec_dst(struct sk_buff *skb);
extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
u8 tos, int oif, struct net_device *dev,
struct in_device *idev, u32 *itag);
-extern void fib_select_default(struct fib_result *res);
+extern struct fib_nh *fib_select_default(struct fib_nh *nh, struct list_head *fa_head,
+ struct fib_table *tb);
#ifdef CONFIG_IP_ROUTE_CLASSID
static inline int fib_num_tclassid_users(struct net *net)
{
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index bf4c809..4c7b55c 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1134,11 +1134,10 @@ int fib_sync_down_dev(struct net_device *dev, int force)
}
/* Must be invoked inside of an RCU protected region. */
-void fib_select_default(struct fib_result *res)
+struct fib_nh *fib_select_default(struct fib_nh *res_nh, struct list_head *fa_head,
+ struct fib_table *tb)
{
struct fib_nh *nh = NULL, *last_resort = NULL;
- struct list_head *fa_head = res->fa_head;
- struct fib_table *tb = res->table;
int order = -1, last_idx = -1;
struct fib_alias *fa;
@@ -1146,12 +1145,12 @@ void fib_select_default(struct fib_result *res)
struct fib_nh *next_nh = &fa->fa_info->fib_nh[0];
if ((next_nh->nh_parent->fib_scope !=
- res->nh->nh_parent->fib_scope) ||
+ res_nh->nh_parent->fib_scope) ||
next_nh->nh_parent->fib_type != RTN_UNICAST)
continue;
if (next_nh->nh_parent->fib_priority >
- res->nh->nh_parent->fib_priority)
+ res_nh->nh_parent->fib_priority)
break;
if (!next_nh->nh_gw || next_nh->nh_scope != RT_SCOPE_LINK)
@@ -1160,11 +1159,11 @@ void fib_select_default(struct fib_result *res)
fib_alias_accessed(fa);
if (nh == NULL) {
- if (next_nh != res->nh)
+ if (next_nh != res_nh)
break;
} else if (!fib_detect_death(nh, order, &last_resort,
&last_idx, tb->tb_default)) {
- res->nh = nh;
+ res_nh = nh;
tb->tb_default = order;
goto out;
}
@@ -1179,16 +1178,16 @@ void fib_select_default(struct fib_result *res)
if (!fib_detect_death(nh, order, &last_resort, &last_idx,
tb->tb_default)) {
- res->nh = nh;
+ res_nh = nh;
tb->tb_default = order;
goto out;
}
if (last_idx >= 0)
- res->nh = last_resort;
+ res_nh = last_resort;
tb->tb_default = last_idx;
out:
- return;
+ return res_nh;
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 597fbc0..9a247ff 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1970,7 +1970,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
if (!res.nh->nh_parent->fib_prefixlen &&
res.table->tb_num_default > 1 &&
type == RTN_UNICAST && !fl4->flowi4_oif)
- fib_select_default(&res);
+ res.nh = fib_select_default(res.nh, res.fa_head, res.table);
if (!fl4->saddr)
fl4->saddr = fib_res_prefsrc(net, &res);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 6/7] ipv4: Make fib_select_multipath() not depend upon fib_result.
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
Pass the arguments and return the result explicitly.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 2 +-
net/ipv4/fib_semantics.c | 15 +++++++--------
net/ipv4/route.c | 4 ++--
3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 8a57513..66f5365 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -287,7 +287,7 @@ extern int fib_sync_down_dev(struct net_device *dev, int force);
extern int fib_sync_down_addr(struct net *net, __be32 local);
extern void fib_update_nh_saddrs(struct net_device *dev);
extern int fib_sync_up(struct net_device *dev);
-extern void fib_select_multipath(struct fib_result *res);
+extern struct fib_nh *fib_select_multipath(struct fib_nh *res_nh);
/* Exported by fib_trie.c */
extern void fib_trie_init(void);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 4c7b55c..f83bef3 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1254,9 +1254,9 @@ int fib_sync_up(struct net_device *dev)
* The algorithm is suboptimal, but it provides really
* fair weighted route distribution.
*/
-void fib_select_multipath(struct fib_result *res)
+struct fib_nh *fib_select_multipath(struct fib_nh *res_nh)
{
- struct fib_info *fi = res->nh->nh_parent;
+ struct fib_info *fi = res_nh->nh_parent;
int w;
spin_lock_bh(&fib_multipath_lock);
@@ -1272,8 +1272,7 @@ void fib_select_multipath(struct fib_result *res)
if (power <= 0) {
spin_unlock_bh(&fib_multipath_lock);
/* Race condition: route has just become dead. */
- res->nh = &fi->fib_nh[0];
- return;
+ return &fi->fib_nh[0];
}
}
@@ -1291,15 +1290,15 @@ void fib_select_multipath(struct fib_result *res)
if (w <= 0) {
nexthop_nh->nh_power--;
fi->fib_power--;
- res->nh = &fi->fib_nh[nhsel];
spin_unlock_bh(&fib_multipath_lock);
- return;
+ return &fi->fib_nh[nhsel];
}
}
} endfor_nexthops(fi);
- /* Race condition: route has just become dead. */
- res->nh = &fi->fib_nh[0];
spin_unlock_bh(&fib_multipath_lock);
+
+ /* Race condition: route has just become dead. */
+ return &fi->fib_nh[0];
}
#endif
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 9a247ff..6b54323 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1461,7 +1461,7 @@ static int ip_mkroute_input(struct sk_buff *skb,
{
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res->nh->nh_parent->fib_nhs > 1)
- fib_select_multipath(res);
+ res->nh = fib_select_multipath(res->nh);
#endif
/* create a routing cache entry */
@@ -1964,7 +1964,7 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
#ifdef CONFIG_IP_ROUTE_MULTIPATH
if (res.nh->nh_parent->fib_nhs > 1 && fl4->flowi4_oif == 0)
- fib_select_multipath(&res);
+ res.nh = fib_select_multipath(res.nh);
else
#endif
if (!res.nh->nh_parent->fib_prefixlen &&
--
1.7.10.4
^ permalink raw reply related
* [PATCH 7/7] ipv4: Perform multihop and default route resolution in check_leaf().
From: David Miller @ 2012-07-28 4:18 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
And thus eliminate two more members of fib_result.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
include/net/ip_fib.h | 2 --
net/ipv4/fib_trie.c | 11 +++++++++--
net/ipv4/route.c | 17 -----------------
3 files changed, 9 insertions(+), 21 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index 66f5365..400c1a7 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -128,8 +128,6 @@ struct fib_table;
struct fib_result {
u32 tclassid;
struct fib_nh *nh;
- struct fib_table *table;
- struct list_head *fa_head;
};
struct fib_result_nl {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 3d1d450..afd5048 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1391,9 +1391,16 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l,
#ifdef CONFIG_IP_FIB_TRIE_STATS
t->stats.semantic_match_passed++;
#endif
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+ if (fi->fib_nhs > 1 && !flp->flowi4_oif)
+ nh = fib_select_multipath(nh);
+ else
+#endif
+ if (!li->plen && tb->tb_num_default > 1 &&
+ fi->fib_type == RTN_UNICAST &&
+ !flp->flowi4_oif)
+ nh = fib_select_default(nh, &li->falh, tb);
res->nh = nh;
- res->table = tb;
- res->fa_head = &li->falh;
if (!(fib_flags & FIB_LOOKUP_NOREF))
atomic_inc(&fi->fib_clntref);
return 0;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6b54323..2d1ede9 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1459,11 +1459,6 @@ static int ip_mkroute_input(struct sk_buff *skb,
struct in_device *in_dev,
__be32 daddr, __be32 saddr, u32 tos)
{
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res->nh->nh_parent->fib_nhs > 1)
- res->nh = fib_select_multipath(res->nh);
-#endif
-
/* create a routing cache entry */
return __mkroute_input(skb, res, in_dev, daddr, saddr, tos);
}
@@ -1814,7 +1809,6 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
res.tclassid = 0;
res.nh = NULL;
- res.table = NULL;
orig_oif = fl4->flowi4_oif;
@@ -1916,7 +1910,6 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
if (fib_lookup(net, fl4, &res)) {
res.nh = NULL;
- res.table = NULL;
if (fl4->flowi4_oif) {
/* Apparently, routing tables are wrong. Assume,
that the destination is on link.
@@ -1962,16 +1955,6 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
goto make_route;
}
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
- if (res.nh->nh_parent->fib_nhs > 1 && fl4->flowi4_oif == 0)
- res.nh = fib_select_multipath(res.nh);
- else
-#endif
- if (!res.nh->nh_parent->fib_prefixlen &&
- res.table->tb_num_default > 1 &&
- type == RTN_UNICAST && !fl4->flowi4_oif)
- res.nh = fib_select_default(res.nh, res.fa_head, res.table);
-
if (!fl4->saddr)
fl4->saddr = fib_res_prefsrc(net, &res);
--
1.7.10.4
^ permalink raw reply related
* Re: more interrupts (lower performance) in bare-metal compared with running VM
From: Eric Dumazet @ 2012-07-28 4:32 UTC (permalink / raw)
To: sheng qiu; +Cc: kvm, linux-kernel, netdev
In-Reply-To: <CAB7xdi=DrE356=U1Jr1Z=ROo2X3XNM5uKcgiZJTKY+EdsTu7gw@mail.gmail.com>
On Fri, 2012-07-27 at 22:09 -0500, sheng qiu wrote:
> Hi all,
>
> i am comparing network throughput performance under bare-metal case
> with that running VM with assigned-device (assigned NIC). i have two
> physical machines (each has a 10Gbit NIC), one is used as remote
> server (run netserver) and the other is used as the target tested one
> (run netperf with different send message size, TCP_STREAM test). the
> remote NIC is connected directly with the tested NIC, both are 10Gbit.
> fore bare-metal case, i enable 1 cpu core, for VM i also configure 1
> vcpu (the memory is sufficient for both bare-metal and VM case). i
> run netperf for 120 seconds and got the following results:
>
> send message interrupts throughput (mbit/s)
> bare-metal 256 10696290 1114.84
> 512 10106786 1391.92
> 1024 10071032 1508.09
> 2048 4560857 3434.65
> 4096 3292200 4762.26
> 8192 3169801 4733.89
> 16384 2780529 4892.6
>
Are these interrupt counts taken on the receiver ?
> VM(assigned NIC) 256 3817904 2249.35
> 512 3599007 4342.81
> 1024 3005601 4134.69
> 2048 2952122 4484
> 4096 2682874 4566.34
> 8192 2786719 4734.39
> 16384 2603835 4540.47
>
> as shown, the interrupts for bare-metal case is much more than the VM
> case for some message size. we also see the throughput for those
> situations is lower than VM case. it's strange that the bare-metal has
> lower performance than the VM case. Does anyone have comments on this?
> i am very confused.
Well, I think you answered to your question. High interrupt rates
are not good for throughput. They might be good for latencies.
Using a VM adds delays and several frames might be delivered per
interrupt.
Using bare metal is faster and only one frame is delivered by NIC per
interrupt.
Try TCP_RR instead of TCP_STREAM for example.
What NIC is it exactly ? It seems it has no coalescing or LRO strategy.
ethtool -k eth0
ethtool -c eth0
What kernel version as used, because 4892 Mbits is not line rate.
^ permalink raw reply
* Re: [PATCH 00/16] Remove the ipv4 routing cache
From: Alexander Duyck @ 2012-07-28 5:45 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, netdev
In-Reply-To: <20120727.211528.1280331721532176100.davem@davemloft.net>
On Fri, Jul 27, 2012 at 9:15 PM, David Miller <davem@davemloft.net> wrote:
> From: David Miller <davem@davemloft.net>
> Date: Thu, 26 Jul 2012 23:02:46 -0700 (PDT)
>
>> Therefore one area of simplification would be to just return a pointer
>> to the FIB nexthop, rather than the fib_info pointer and the nexthop
>> index. We can get to the fib_info, if we need to, via the nh_parent
>> pointer of the nexthop.
>
> So I'm about to post an RFC set of patches which show this kind
> of simplification. It gets fib_result down to two members:
>
> u32 tclassid;
> struct fib_nh *nh;
>
> If I could get rid of that tclassid it would be really nice. But
> that's hard because the tclassid is fetched from the fib_rule and
> all of that lookup path is abstracted behind a common layer
> that's shared between ipv4 and ipv6 so it's a bit of work changing
> arg conventions.
>
> These changes help, but only ever so slightly, in my testing.
I probably won't be able to do any real testing for the patches until
Monday, or at least I may let somebody else test the patches first
just to make sure they don't panic the system before I do anything
with them.
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH 0/7] Deconstruct struct fib_result
From: Alexander Duyck @ 2012-07-28 6:41 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, netdev
In-Reply-To: <20120727.211814.1628871775065221675.davem@davemloft.net>
On Fri, Jul 27, 2012 at 9:18 PM, David Miller <davem@davemloft.net> wrote:
>
> This patch set tries to move towards reducing struct fib_result down
> to it's absolute minimum.
>
> The eventual idea is to make it so that fib_lookup() simply
> returns a "struct fib_nh *" and pointer encoded errnos, instead
> of writing into a complicated structure as the return value on
> the stack as is done now.
>
> Signed-off-by: David S. Miller <davem@davemloft.net>
I was wondering why you couldn't wrap the tclassid value in result
with an #ifdef CONFIG_IP_ROUTE_CLASSID so that it is stripped if you
don't have that defined? I did a quick grep through the kernel and it
seems like there are only a few spots that are accessing it without
the define being checked and most of those are just setting it to 0.
Also is there any specific reason to be passing the trie pointer to
check_leaf? From what I can tell it looks like that it is just used
for updating the trie stats that are stripped if we want performance,
and the trie pointer should be retrievable via the fib table pointer
if we really wanted it anyway.
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH 0/7] Deconstruct struct fib_result
From: David Miller @ 2012-07-28 6:55 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
In-Reply-To: <CAKgT0UcuvFGJTCxMwVa+rEnQHaNg0V6EH4XpO29fXuFADSBpAQ@mail.gmail.com>
From: Alexander Duyck <alexander.duyck@gmail.com>
Date: Fri, 27 Jul 2012 23:41:50 -0700
> I was wondering why you couldn't wrap the tclassid value in result
> with an #ifdef CONFIG_IP_ROUTE_CLASSID so that it is stripped if you
> don't have that defined?
I am so over these feature ifdefs in the routing code, because
realistically everyone turns all the crap on. Therefore we have to be
optimal when it's all on.
^ permalink raw reply
* Re: [PATCH net-next] GRE over IPv6
From: Dmitry Kozlov @ 2012-07-28 7:17 UTC (permalink / raw)
To: David Miller; +Cc: netdev
>Why have you submitted these patches two times?
Sorry, that was a mistake...
^ permalink raw reply
* Re: [PATCH v2] ppp: add 64 bit stats
From: Eric Dumazet @ 2012-07-28 7:39 UTC (permalink / raw)
To: Kevin Groeneveld; +Cc: netdev
In-Reply-To: <1343446733-4791-1-git-send-email-kgroeneveld@gmail.com>
On Fri, 2012-07-27 at 23:38 -0400, Kevin Groeneveld wrote:
> Add 64 bit stats to ppp driver. The 64 bit stats include tx_bytes,
> rx_bytes, tx_packets and rx_packets. Other stats are still 32 bit.
> The 64 bit stats can be retrieved via the ndo_get_stats operation. The
> SIOCGPPPSTATS ioctl is still 32 bit stats only.
>
> Signed-off-by: Kevin Groeneveld <kgroeneveld@gmail.com>
> ---
> v2: - do not use percpu variables for stats
> - use ppp_recv_lock/ppp_xmit_lock when reading 64 bit stats
>
Seems fine to me this time ;)
Acked-by: Eric Dumazet <edumazet@google.com>
^ permalink raw reply
* [PATCH net-next v2] GRE over IPv6
From: Dmitry Kozlov @ 2012-07-28 7:35 UTC (permalink / raw)
To: netdev
GRE over IPv6 implementation.
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
---
Changes:
Initialize nt->dev before calling ip6gre_tnl_link_config in
ip6gre_newlink.
include/linux/if_arp.h | 1 +
include/linux/if_tunnel.h | 3 ++
include/linux/ip6_tunnel.h | 18 +++++++++
include/net/ip6_tunnel.h | 40 ++++++++++++++++++++-
include/net/ipv6.h | 1 +
net/ipv6/Kconfig | 16 ++++++++
net/ipv6/Makefile | 1 +
net/ipv6/ip6_tunnel.c | 86 +++++++++++++++++++++++++++++++------------
8 files changed, 141 insertions(+), 25 deletions(-)
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index f0e69c6..9adcc29 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -92,6 +92,7 @@
#define ARPHRD_PHONET 820 /* PhoNet media type */
#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
#define ARPHRD_CAIF 822 /* CAIF media type */
+#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 5efff60..8c5035a 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -75,6 +75,9 @@ enum {
IFLA_GRE_TTL,
IFLA_GRE_TOS,
IFLA_GRE_PMTUDISC,
+ IFLA_GRE_ENCAP_LIMIT,
+ IFLA_GRE_FLOWINFO,
+ IFLA_GRE_FLAGS,
__IFLA_GRE_MAX,
};
diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
index bf22b03..1efe2e0 100644
--- a/include/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -31,4 +31,22 @@ struct ip6_tnl_parm {
struct in6_addr raddr; /* remote tunnel end-point address */
};
+struct ip6_tnl_parm2 {
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface */
+ __u8 proto; /* tunnel protocol */
+ __u8 encap_limit; /* encapsulation limit for tunnel */
+ __u8 hop_limit; /* hop limit for tunnel */
+ __be32 flowinfo; /* traffic class and flowlabel for tunnel */
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point address */
+ struct in6_addr raddr; /* remote tunnel end-point address */
+
+ __be16 i_flags;
+ __be16 o_flags;
+ __be32 i_key;
+ __be32 o_key;
+};
+
+
#endif
diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h
index 358fb86..8400ba0 100644
--- a/include/net/ip6_tunnel.h
+++ b/include/net/ip6_tunnel.h
@@ -5,6 +5,8 @@
#include <linux/netdevice.h>
#include <linux/ip6_tunnel.h>
+#define IP6TUNNEL_ERR_TIMEO (30*HZ)
+
/* capable of sending packets */
#define IP6_TNL_F_CAP_XMIT 0x10000
/* capable of receiving packets */
@@ -13,14 +15,40 @@
#define IP6_TNL_F_CAP_PER_PACKET 0x40000
/* IPv6 tunnel */
+struct __ip6_tnl_parm
+{
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface */
+ __u8 proto; /* tunnel protocol */
+ __u8 encap_limit; /* encapsulation limit for tunnel */
+ __u8 hop_limit; /* hop limit for tunnel */
+ __be32 flowinfo; /* traffic class and flowlabel for tunnel */
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point address */
+ struct in6_addr raddr; /* remote tunnel end-point address */
+
+ __be16 i_flags;
+ __be16 o_flags;
+ __be32 i_key;
+ __be32 o_key;
+};
struct ip6_tnl {
struct ip6_tnl __rcu *next; /* next tunnel in list */
struct net_device *dev; /* virtual device associated with tunnel */
- struct ip6_tnl_parm parms; /* tunnel configuration parameters */
+ struct __ip6_tnl_parm parms; /* tunnel configuration parameters */
struct flowi fl; /* flowi template for xmit */
struct dst_entry *dst_cache; /* cached dst */
u32 dst_cookie;
+
+ int err_count;
+ unsigned long err_time;
+
+ /* These fields used only by GRE */
+ __u32 i_seqno; /* The last seen seqno */
+ __u32 o_seqno; /* The last output seqno */
+ int hlen; /* Precalculated GRE header length */
+ int mlink;
};
/* Tunnel encapsulation limit destination sub-option */
@@ -31,4 +59,14 @@ struct ipv6_tlv_tnl_enc_lim {
__u8 encap_limit; /* tunnel encapsulation limit */
} __packed;
+struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t);
+void ip6_tnl_dst_reset(struct ip6_tnl *t);
+void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst);
+int ip6_tnl_rcv_ctl(struct ip6_tnl *t, const struct in6_addr *laddr,
+ const struct in6_addr *raddr);
+int ip6_tnl_xmit_ctl(struct ip6_tnl *t);
+__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 * raw);
+__u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr,
+ const struct in6_addr *raddr);
+
#endif
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 01c34b3..6d01fb0 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -34,6 +34,7 @@
#define NEXTHDR_IPV6 41 /* IPv6 in IPv6 */
#define NEXTHDR_ROUTING 43 /* Routing header. */
#define NEXTHDR_FRAGMENT 44 /* Fragmentation/reassembly header. */
+#define NEXTHDR_GRE 47 /* GRE header. */
#define NEXTHDR_ESP 50 /* Encapsulating security payload. */
#define NEXTHDR_AUTH 51 /* Authentication header. */
#define NEXTHDR_ICMP 58 /* ICMP for IPv6. */
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 5728695..4f7fe72 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -201,6 +201,22 @@ config IPV6_TUNNEL
If unsure, say N.
+config IPV6_GRE
+ tristate "IPv6: GRE tunnel"
+ select IPV6_TUNNEL
+ ---help---
+ Tunneling means encapsulating data of one protocol type within
+ another protocol and sending it over a channel that understands the
+ encapsulating protocol. This particular tunneling driver implements
+ GRE (Generic Routing Encapsulation) and at this time allows
+ encapsulating of IPv4 or IPv6 over existing IPv6 infrastructure.
+ This driver is useful if the other endpoint is a Cisco router: Cisco
+ likes GRE much better than the other Linux tunneling driver ("IP
+ tunneling" above). In addition, GRE allows multicast redistribution
+ through the tunnel.
+
+ Saying M here will produce a module called ip6_gre. If unsure, say N.
+
config IPV6_MULTIPLE_TABLES
bool "IPv6: Multiple Routing Tables"
depends on EXPERIMENTAL
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 686934a..b6d3f79 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_SIT) += sit.o
obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
+obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
obj-y += addrconf_core.o exthdrs_core.o
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 9a1d5fe..fc24522 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -126,7 +126,7 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev)
* Locking : hash tables are protected by RCU and RTNL
*/
-static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
+struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
{
struct dst_entry *dst = t->dst_cache;
@@ -139,20 +139,23 @@ static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t)
return dst;
}
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_check);
-static inline void ip6_tnl_dst_reset(struct ip6_tnl *t)
+void ip6_tnl_dst_reset(struct ip6_tnl *t)
{
dst_release(t->dst_cache);
t->dst_cache = NULL;
}
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset);
-static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
+void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst)
{
struct rt6_info *rt = (struct rt6_info *) dst;
t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
dst_release(t->dst_cache);
t->dst_cache = dst;
}
+EXPORT_SYMBOL_GPL(ip6_tnl_dst_store);
/**
* ip6_tnl_lookup - fetch tunnel matching the end-point addresses
@@ -200,7 +203,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
**/
static struct ip6_tnl __rcu **
-ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p)
+ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p)
{
const struct in6_addr *remote = &p->raddr;
const struct in6_addr *local = &p->laddr;
@@ -267,7 +270,7 @@ static void ip6_dev_free(struct net_device *dev)
* created tunnel or NULL
**/
-static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p)
+static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
{
struct net_device *dev;
struct ip6_tnl *t;
@@ -322,7 +325,7 @@ failed:
**/
static struct ip6_tnl *ip6_tnl_locate(struct net *net,
- struct ip6_tnl_parm *p, int create)
+ struct __ip6_tnl_parm *p, int create)
{
const struct in6_addr *remote = &p->raddr;
const struct in6_addr *local = &p->laddr;
@@ -374,8 +377,7 @@ ip6_tnl_dev_uninit(struct net_device *dev)
* else index to encapsulation limit
**/
-static __u16
-parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
+__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 * raw)
{
const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw;
__u8 nexthdr = ipv6h->nexthdr;
@@ -425,6 +427,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw)
}
return 0;
}
+EXPORT_SYMBOL(ip6_tnl_parse_tlv_enc_lim);
/**
* ip6_tnl_err - tunnel error handler
@@ -480,7 +483,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
case ICMPV6_PARAMPROB:
teli = 0;
if ((*code) == ICMPV6_HDR_FIELD)
- teli = parse_tlv_tnl_enc_lim(skb, skb->data);
+ teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data);
if (teli && teli == *info - 2) {
tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
@@ -693,11 +696,11 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
IP6_ECN_set_ce(ipv6_hdr(skb));
}
-static __u32 ip6_tnl_get_cap(struct ip6_tnl *t,
+__u32 ip6_tnl_get_cap(struct ip6_tnl *t,
const struct in6_addr *laddr,
const struct in6_addr *raddr)
{
- struct ip6_tnl_parm *p = &t->parms;
+ struct __ip6_tnl_parm *p = &t->parms;
int ltype = ipv6_addr_type(laddr);
int rtype = ipv6_addr_type(raddr);
__u32 flags = 0;
@@ -715,13 +718,14 @@ static __u32 ip6_tnl_get_cap(struct ip6_tnl *t,
}
return flags;
}
+EXPORT_SYMBOL(ip6_tnl_get_cap);
/* called with rcu_read_lock() */
-static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
+int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
const struct in6_addr *laddr,
const struct in6_addr *raddr)
{
- struct ip6_tnl_parm *p = &t->parms;
+ struct __ip6_tnl_parm *p = &t->parms;
int ret = 0;
struct net *net = dev_net(t->dev);
@@ -740,6 +744,7 @@ static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
}
return ret;
}
+EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
/**
* ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally
@@ -859,9 +864,9 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
}
-static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
+int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
{
- struct ip6_tnl_parm *p = &t->parms;
+ struct __ip6_tnl_parm *p = &t->parms;
int ret = 0;
struct net *net = dev_net(t->dev);
@@ -885,6 +890,8 @@ static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
}
return ret;
}
+EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl);
+
/**
* ip6_tnl_xmit2 - encapsulate packet and send
* @skb: the outgoing socket buffer
@@ -1085,7 +1092,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
!ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h))
return -1;
- offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb));
+ offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
if (offset > 0) {
struct ipv6_tlv_tnl_enc_lim *tel;
tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset];
@@ -1152,7 +1159,7 @@ tx_err:
static void ip6_tnl_link_config(struct ip6_tnl *t)
{
struct net_device *dev = t->dev;
- struct ip6_tnl_parm *p = &t->parms;
+ struct __ip6_tnl_parm *p = &t->parms;
struct flowi6 *fl6 = &t->fl.u.ip6;
memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
@@ -1215,7 +1222,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
**/
static int
-ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
+ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
{
t->parms.laddr = p->laddr;
t->parms.raddr = p->raddr;
@@ -1230,6 +1237,32 @@ ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p)
return 0;
}
+static void ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u)
+{
+ p->laddr = u->laddr;
+ p->raddr = u->raddr;
+ p->flags = u->flags;
+ p->hop_limit = u->hop_limit;
+ p->encap_limit = u->encap_limit;
+ p->flowinfo = u->flowinfo;
+ p->link = u->link;
+ p->proto = u->proto;
+ memcpy(p->name, u->name, sizeof(u->name));
+}
+
+static void ip6_tnl_parm_to_user(struct ip6_tnl_parm *u, const struct __ip6_tnl_parm *p)
+{
+ u->laddr = p->laddr;
+ u->raddr = p->raddr;
+ u->flags = p->flags;
+ u->hop_limit = p->hop_limit;
+ u->encap_limit = p->encap_limit;
+ u->flowinfo = p->flowinfo;
+ u->link = p->link;
+ u->proto = p->proto;
+ memcpy(u->name, p->name, sizeof(u->name));
+}
+
/**
* ip6_tnl_ioctl - configure ipv6 tunnels from userspace
* @dev: virtual device associated with tunnel
@@ -1263,6 +1296,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int err = 0;
struct ip6_tnl_parm p;
+ struct __ip6_tnl_parm p1;
struct ip6_tnl *t = NULL;
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
@@ -1274,11 +1308,12 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
err = -EFAULT;
break;
}
- t = ip6_tnl_locate(net, &p, 0);
+ ip6_tnl_parm_from_user(&p1, &p);
+ t = ip6_tnl_locate(net, &p1, 0);
}
if (t == NULL)
t = netdev_priv(dev);
- memcpy(&p, &t->parms, sizeof (p));
+ ip6_tnl_parm_to_user(&p, &t->parms);
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
err = -EFAULT;
}
@@ -1295,7 +1330,8 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP &&
p.proto != 0)
break;
- t = ip6_tnl_locate(net, &p, cmd == SIOCADDTUNNEL);
+ ip6_tnl_parm_from_user(&p1, &p);
+ t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
@@ -1307,13 +1343,14 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
ip6_tnl_unlink(ip6n, t);
synchronize_net();
- err = ip6_tnl_change(t, &p);
+ err = ip6_tnl_change(t, &p1);
ip6_tnl_link(ip6n, t);
netdev_state_change(dev);
}
if (t) {
err = 0;
- if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p)))
+ ip6_tnl_parm_to_user(&p, &t->parms);
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p)))
err = -EFAULT;
} else
@@ -1329,7 +1366,8 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
break;
err = -ENOENT;
- if ((t = ip6_tnl_locate(net, &p, 0)) == NULL)
+ ip6_tnl_parm_from_user(&p1, &p);
+ if ((t = ip6_tnl_locate(net, &p1, 0)) == NULL)
break;
err = -EPERM;
if (t->dev == ip6n->fb_tnl_dev)
^ permalink raw reply related
* [PATCH iproute2 v2] GRE over IPv6 tunnel support
From: Dmitry Kozlov @ 2012-07-28 7:38 UTC (permalink / raw)
To: netdev
GRE over IPv6 tunnel support.
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
---
Changes:
Implemented 'ip link' family of commands to manage
ip6gre/ip6gretap tunnels.
include/linux/if_arp.h | 15 ++
include/linux/if_tunnel.h | 21 +++
include/linux/ip6_tunnel.h | 18 ++
ip/Makefile | 2 +-
ip/ip6tunnel.c | 133 +++++++++++++--
ip/link_gre6.c | 397 ++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 567 insertions(+), 19 deletions(-)
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index d2de0f9..9adcc29 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -92,6 +92,7 @@
#define ARPHRD_PHONET 820 /* PhoNet media type */
#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
#define ARPHRD_CAIF 822 /* CAIF media type */
+#define ARPHRD_IP6GRE 823 /* GRE over IPv6 */
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
@@ -154,5 +155,19 @@ struct arphdr {
};
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
+{
+ return (struct arphdr *)skb_network_header(skb);
+}
+
+static inline int arp_hdr_len(struct net_device *dev)
+{
+ /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
+ return sizeof(struct arphdr) + (dev->addr_len + sizeof(u32)) * 2;
+}
+#endif
#endif /* _LINUX_IF_ARP_H */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 8c819b8..8c5035a 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -4,6 +4,10 @@
#include <linux/types.h>
#include <asm/byteorder.h>
+#ifdef __KERNEL__
+#include <linux/ip.h>
+#include <linux/in6.h>
+#endif
#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
@@ -71,9 +75,26 @@ enum {
IFLA_GRE_TTL,
IFLA_GRE_TOS,
IFLA_GRE_PMTUDISC,
+ IFLA_GRE_ENCAP_LIMIT,
+ IFLA_GRE_FLOWINFO,
+ IFLA_GRE_FLAGS,
__IFLA_GRE_MAX,
};
#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
+/* VTI-mode i_flags */
+#define VTI_ISVTI 0x0001
+
+enum {
+ IFLA_VTI_UNSPEC,
+ IFLA_VTI_LINK,
+ IFLA_VTI_IKEY,
+ IFLA_VTI_OKEY,
+ IFLA_VTI_LOCAL,
+ IFLA_VTI_REMOTE,
+ __IFLA_VTI_MAX,
+};
+
+#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
#endif /* _IF_TUNNEL_H_ */
diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
index bf22b03..1efe2e0 100644
--- a/include/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -31,4 +31,22 @@ struct ip6_tnl_parm {
struct in6_addr raddr; /* remote tunnel end-point address */
};
+struct ip6_tnl_parm2 {
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface */
+ __u8 proto; /* tunnel protocol */
+ __u8 encap_limit; /* encapsulation limit for tunnel */
+ __u8 hop_limit; /* hop limit for tunnel */
+ __be32 flowinfo; /* traffic class and flowlabel for tunnel */
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point address */
+ struct in6_addr raddr; /* remote tunnel end-point address */
+
+ __be16 i_flags;
+ __be16 o_flags;
+ __be32 i_key;
+ __be32 o_key;
+};
+
+
#endif
diff --git a/ip/Makefile b/ip/Makefile
index e029ea1..9c54a0b 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -3,7 +3,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
- iplink_macvlan.o iplink_macvtap.o ipl2tp.o
+ iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_gre6.o
RTMONOBJ=rtmon.o
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
index c9720eb..399a579 100644
--- a/ip/ip6tunnel.c
+++ b/ip/ip6tunnel.c
@@ -48,11 +48,12 @@ static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change | del | show } [ NAME ]\n");
- fprintf(stderr, " [ mode { ip6ip6 | ipip6 | any } ]\n");
+ fprintf(stderr, " [ mode { ip6ip6 | ipip6 | ip6gre | any } ]\n");
fprintf(stderr, " [ remote ADDR local ADDR ] [ dev PHYS_DEV ]\n");
fprintf(stderr, " [ encaplimit ELIM ]\n");
fprintf(stderr ," [ hoplimit TTL ] [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
fprintf(stderr, " [ dscp inherit ]\n");
+ fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Where: NAME := STRING\n");
fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
@@ -60,12 +61,13 @@ static void usage(void)
IPV6_DEFAULT_TNL_ENCAP_LIMIT);
fprintf(stderr, " TTL := 0..255 (default=%d)\n",
DEFAULT_TNL_HOP_LIMIT);
- fprintf(stderr, " TOS := { 0x0..0xff | inherit }\n");
+ fprintf(stderr, " TCLASS := { 0x0..0xff | inherit }\n");
fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+ fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n");
exit(-1);
}
-static void print_tunnel(struct ip6_tnl_parm *p)
+static void print_tunnel(struct ip6_tnl_parm2 *p)
{
char remote[64];
char local[64];
@@ -104,9 +106,29 @@ static void print_tunnel(struct ip6_tnl_parm *p)
if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
printf(" dscp inherit");
+
+ if (p->proto == IPPROTO_GRE) {
+ if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
+ printf(" key %u", ntohl(p->i_key));
+ else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+ if (p->i_flags&GRE_KEY)
+ printf(" ikey %u ", ntohl(p->i_key));
+ if (p->o_flags&GRE_KEY)
+ printf(" okey %u ", ntohl(p->o_key));
+ }
+
+ if (p->i_flags&GRE_SEQ)
+ printf("%s Drop packets out of sequence.\n", _SL_);
+ if (p->i_flags&GRE_CSUM)
+ printf("%s Checksum in received packet is required.", _SL_);
+ if (p->o_flags&GRE_SEQ)
+ printf("%s Sequence packets on output.", _SL_);
+ if (p->o_flags&GRE_CSUM)
+ printf("%s Checksum output packets.", _SL_);
+ }
}
-static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
+static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
{
int count = 0;
char medium[IFNAMSIZ];
@@ -124,6 +146,9 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
strcmp(*argv, "ipip6") == 0 ||
strcmp(*argv, "ip4ip6") == 0)
p->proto = IPPROTO_IPIP;
+ else if (strcmp(*argv, "ip6gre") == 0 ||
+ strcmp(*argv, "gre/ipv6") == 0)
+ p->proto = IPPROTO_GRE;
else if (strcmp(*argv, "any/ipv6") == 0 ||
strcmp(*argv, "any") == 0)
p->proto = 0;
@@ -199,6 +224,60 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
if (strcmp(*argv, "inherit") != 0)
invarg("not inherit", *argv);
p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ } else if (strcmp(*argv, "key") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value of \"key\"\n");
+ exit(-1);
+ }
+ p->i_key = p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "ikey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value of \"ikey\"\n");
+ exit(-1);
+ }
+ p->i_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "okey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value of \"okey\"\n");
+ exit(-1);
+ }
+ p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "seq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "iseq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "oseq") == 0) {
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "csum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "icsum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "ocsum") == 0) {
+ p->o_flags |= GRE_CSUM;
} else {
if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
@@ -209,7 +288,7 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
duparg2("name", *argv);
strncpy(p->name, *argv, IFNAMSIZ - 1);
if (cmd == SIOCCHGTUNNEL && count == 0) {
- struct ip6_tnl_parm old_p;
+ struct ip6_tnl_parm2 old_p;
memset(&old_p, 0, sizeof(old_p));
if (tnl_get_ioctl(*argv, &old_p))
return -1;
@@ -227,7 +306,7 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm *p)
return 0;
}
-static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
+static void ip6_tnl_parm_init(struct ip6_tnl_parm2 *p, int apply_default)
{
memset(p, 0, sizeof(*p));
p->proto = IPPROTO_IPV6;
@@ -241,8 +320,8 @@ static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int apply_default)
* @p1: user specified parameter
* @p2: database entry
*/
-static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
- const struct ip6_tnl_parm *p2)
+static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1,
+ const struct ip6_tnl_parm2 *p2)
{
return ((!p1->link || p1->link == p2->link) &&
(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
@@ -260,7 +339,7 @@ static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
(!p1->flags || (p1->flags & p2->flags)));
}
-static int do_tunnels_list(struct ip6_tnl_parm *p)
+static int do_tunnels_list(struct ip6_tnl_parm2 *p)
{
char buf[512];
int err = -1;
@@ -284,7 +363,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
rx_fifo, rx_frame,
tx_bytes, tx_packets, tx_errs, tx_drops,
tx_fifo, tx_colls, tx_carrier, rx_multi;
- struct ip6_tnl_parm p1;
+ struct ip6_tnl_parm2 p1;
char *ptr;
buf[sizeof(buf) - 1] = '\0';
@@ -309,10 +388,12 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
fprintf(stderr, "Failed to get type of [%s]\n", name);
continue;
}
- if (type != ARPHRD_TUNNEL6)
+ if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE)
continue;
memset(&p1, 0, sizeof(p1));
ip6_tnl_parm_init(&p1, 0);
+ if (type == ARPHRD_IP6GRE)
+ p1.proto = IPPROTO_GRE;
strcpy(p1.name, name);
p1.link = ll_name_to_index(p1.name);
if (p1.link == 0)
@@ -343,7 +424,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
static int do_show(int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ll_init_map(&rth);
ip6_tnl_parm_init(&p, 0);
@@ -366,28 +447,44 @@ static int do_show(int argc, char **argv)
static int do_add(int cmd, int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, cmd, &p) < 0)
return -1;
- return tnl_add_ioctl(cmd,
- cmd == SIOCCHGTUNNEL && p.name[0] ?
- p.name : "ip6tnl0", p.name, &p);
+ switch (p.proto) {
+ case IPPROTO_IPIP:
+ case IPPROTO_IPV6:
+ return tnl_add_ioctl(cmd, "ip6tnl0", p.name, &p);
+ case IPPROTO_GRE:
+ return tnl_add_ioctl(cmd, "ip6gre0", p.name, &p);
+ default:
+ fprintf(stderr, "cannot determine tunnel mode (ip6ip6, ipip6 or gre)\n");
+ }
+ return -1;
}
static int do_del(int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
return -1;
- return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name, &p);
+ switch (p.proto) {
+ case IPPROTO_IPIP:
+ case IPPROTO_IPV6:
+ return tnl_del_ioctl("ip6tnl0", p.name, &p);
+ case IPPROTO_GRE:
+ return tnl_del_ioctl("ip6gre0", p.name, &p);
+ default:
+ return tnl_del_ioctl(p.name, p.name, &p);
+ }
+ return -1;
}
int do_ip6tunnel(int argc, char **argv)
diff --git a/ip/link_gre6.c b/ip/link_gre6.c
new file mode 100644
index 0000000..3ffd3ce
--- /dev/null
+++ b/ip/link_gre6.c
@@ -0,0 +1,397 @@
+/*
+ * link_gre.c gre driver module
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000)
+#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
+
+#define DEFAULT_TNL_HOP_LIMIT (64)
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip link { add | set | change | replace | del } NAME\n");
+ fprintf(stderr, " type { ip6gre | ip6gretap } [ remote ADDR ] [ local ADDR ]\n");
+ fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n");
+ fprintf(stderr, " [ hoplimit TTL ] [ encaplimit ELIM ]\n");
+ fprintf(stderr, " [ tclass TCLASS ] [ flowlabel FLOWLABEL ]\n");
+ fprintf(stderr, " [ dscp inherit ] [ dev PHYS_DEV ]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where: NAME := STRING\n");
+ fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
+ fprintf(stderr, " TTL := { 0..255 } (default=%d)\n",
+ DEFAULT_TNL_HOP_LIMIT);
+ fprintf(stderr, " KEY := { DOTTED_QUAD | NUMBER }\n");
+ fprintf(stderr, " ELIM := { none | 0..255 }(default=%d)\n",
+ IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+ fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff | inherit }\n");
+ exit(-1);
+}
+
+static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg i;
+ char buf[1024];
+ } req;
+ struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+ struct rtattr *tb[IFLA_MAX + 1];
+ struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+ struct rtattr *greinfo[IFLA_GRE_MAX + 1];
+ __u16 iflags = 0;
+ __u16 oflags = 0;
+ unsigned ikey = 0;
+ unsigned okey = 0;
+ struct in6_addr raddr = IN6ADDR_ANY_INIT;
+ struct in6_addr laddr = IN6ADDR_ANY_INIT;
+ unsigned link = 0;
+ unsigned flowinfo = 0;
+ unsigned flags = 0;
+ __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
+ __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
+ int len;
+
+ if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+ req.i.ifi_family = preferred_family;
+ req.i.ifi_index = ifi->ifi_index;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+ fprintf(stderr,
+ "Failed to get existing tunnel info.\n");
+ return -1;
+ }
+
+ len = req.n.nlmsg_len;
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ goto get_failed;
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+ if (!tb[IFLA_LINKINFO])
+ goto get_failed;
+
+ parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
+
+ if (!linkinfo[IFLA_INFO_DATA])
+ goto get_failed;
+
+ parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
+ linkinfo[IFLA_INFO_DATA]);
+
+ if (greinfo[IFLA_GRE_IKEY])
+ ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
+
+ if (greinfo[IFLA_GRE_OKEY])
+ okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
+
+ if (greinfo[IFLA_GRE_IFLAGS])
+ iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
+
+ if (greinfo[IFLA_GRE_OFLAGS])
+ oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
+
+ if (greinfo[IFLA_GRE_LOCAL])
+ memcpy(&laddr, RTA_DATA(greinfo[IFLA_GRE_LOCAL]), sizeof(laddr));
+
+ if (greinfo[IFLA_GRE_REMOTE])
+ memcpy(&raddr, RTA_DATA(greinfo[IFLA_GRE_REMOTE]), sizeof(raddr));
+
+ if (greinfo[IFLA_GRE_TTL])
+ hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
+
+ if (greinfo[IFLA_GRE_LINK])
+ link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
+
+ if (greinfo[IFLA_GRE_ENCAP_LIMIT])
+ encap_limit = rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]);
+
+ if (greinfo[IFLA_GRE_FLOWINFO])
+ flowinfo = rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]);
+
+ if (greinfo[IFLA_GRE_FLAGS])
+ flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
+ }
+
+ while (argc > 0) {
+ if (!matches(*argv, "key")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ iflags |= GRE_KEY;
+ oflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0) < 0) {
+ fprintf(stderr,
+ "Invalid value for \"key\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+
+ ikey = okey = uval;
+ } else if (!matches(*argv, "ikey")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ iflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value of \"ikey\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+ ikey = uval;
+ } else if (!matches(*argv, "okey")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ oflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value of \"okey\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+ okey = uval;
+ } else if (!matches(*argv, "seq")) {
+ iflags |= GRE_SEQ;
+ oflags |= GRE_SEQ;
+ } else if (!matches(*argv, "iseq")) {
+ iflags |= GRE_SEQ;
+ } else if (!matches(*argv, "oseq")) {
+ oflags |= GRE_SEQ;
+ } else if (!matches(*argv, "csum")) {
+ iflags |= GRE_CSUM;
+ oflags |= GRE_CSUM;
+ } else if (!matches(*argv, "icsum")) {
+ iflags |= GRE_CSUM;
+ } else if (!matches(*argv, "ocsum")) {
+ oflags |= GRE_CSUM;
+ } else if (!matches(*argv, "remote")) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_prefix(&addr, *argv, preferred_family);
+ if (addr.family == AF_UNSPEC)
+ invarg("\"remote\" address family is AF_UNSPEC", *argv);
+ memcpy(&raddr, &addr.data, sizeof(raddr));
+ } else if (!matches(*argv, "local")) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_prefix(&addr, *argv, preferred_family);
+ if (addr.family == AF_UNSPEC)
+ invarg("\"local\" address family is AF_UNSPEC", *argv);
+ memcpy(&laddr, &addr.data, sizeof(laddr));
+ } else if (!matches(*argv, "dev")) {
+ NEXT_ARG();
+ link = if_nametoindex(*argv);
+ if (link == 0)
+ exit(-1);
+ } else if (!matches(*argv, "ttl") ||
+ !matches(*argv, "hoplimit")) {
+ __u8 uval;
+ NEXT_ARG();
+ if (get_u8(&uval, *argv, 0))
+ invarg("invalid TTL", *argv);
+ hop_limit = uval;
+ } else if (!matches(*argv, "tos") ||
+ !matches(*argv, "tclass") ||
+ !matches(*argv, "dsfield")) {
+ __u8 uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") == 0)
+ flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+ else {
+ if (get_u8(&uval, *argv, 16))
+ invarg("invalid TClass", *argv);
+ flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
+ flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
+ }
+ } else if (strcmp(*argv, "flowlabel") == 0 ||
+ strcmp(*argv, "fl") == 0) {
+ __u32 uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") == 0)
+ flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
+ else {
+ if (get_u32(&uval, *argv, 16))
+ invarg("invalid Flowlabel", *argv);
+ if (uval > 0xFFFFF)
+ invarg("invalid Flowlabel", *argv);
+ flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
+ flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+ }
+ } else if (strcmp(*argv, "dscp") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0)
+ invarg("not inherit", *argv);
+ flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ } else
+ usage();
+ argc--; argv++;
+ }
+
+ addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
+ addattr32(n, 1024, IFLA_GRE_OKEY, okey);
+ addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
+ addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
+ addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
+ addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
+ if (link)
+ addattr32(n, 1024, IFLA_GRE_LINK, link);
+ addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
+ addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
+ addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
+ addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4);
+
+ return 0;
+}
+
+static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+ char s1[1024];
+ char s2[64];
+ const char *local = "any";
+ const char *remote = "any";
+ unsigned iflags = 0;
+ unsigned oflags = 0;
+ unsigned flags = 0;
+ unsigned flowinfo = 0;
+ struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
+
+ if (!tb)
+ return;
+
+ if (tb[IFLA_GRE_FLAGS])
+ flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
+
+ if (tb[IFLA_GRE_FLOWINFO])
+ flags = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
+
+ if (tb[IFLA_GRE_REMOTE]) {
+ struct in6_addr addr;
+ memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]), sizeof(addr));
+
+ if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+ remote = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
+ }
+
+ fprintf(f, "remote %s ", remote);
+
+ if (tb[IFLA_GRE_LOCAL]) {
+ struct in6_addr addr;
+ memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]), sizeof(addr));
+
+ if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+ local = format_host(AF_INET6, sizeof(addr), &addr, s1, sizeof(s1));
+ }
+
+ fprintf(f, "local %s ", local);
+
+ if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
+ unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
+ const char *n = if_indextoname(link, s2);
+
+ if (n)
+ fprintf(f, "dev %s ", n);
+ else
+ fprintf(f, "dev %u ", link);
+ }
+
+ if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+ fprintf(f, "hoplimit %d ", rta_getattr_u8(tb[IFLA_GRE_TTL]));
+
+ if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+ fprintf(f, "encaplimit none ");
+ else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
+ int encap_limit = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
+
+ fprintf(f, "encaplimit %d ", encap_limit);
+ }
+
+ if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+ fprintf(f, "flowlabel inherit ");
+ else
+ fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL));
+
+ if (flags & IP6_TNL_F_RCV_DSCP_COPY)
+ fprintf(f, "dscp inherit ");
+
+ if (tb[IFLA_GRE_IFLAGS])
+ iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
+
+ if (tb[IFLA_GRE_OFLAGS])
+ oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
+
+ if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
+ inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
+ fprintf(f, "ikey %s ", s2);
+ }
+
+ if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
+ inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
+ fprintf(f, "okey %s ", s2);
+ }
+
+ if (iflags & GRE_SEQ)
+ fputs("iseq ", f);
+ if (oflags & GRE_SEQ)
+ fputs("oseq ", f);
+ if (iflags & GRE_CSUM)
+ fputs("icsum ", f);
+ if (oflags & GRE_CSUM)
+ fputs("ocsum ", f);
+}
+
+struct link_util ip6gre_link_util = {
+ .id = "ip6gre",
+ .maxattr = IFLA_GRE_MAX,
+ .parse_opt = gre_parse_opt,
+ .print_opt = gre_print_opt,
+};
+
+struct link_util ip6gretap_link_util = {
+ .id = "ip6gretap",
+ .maxattr = IFLA_GRE_MAX,
+ .parse_opt = gre_parse_opt,
+ .print_opt = gre_print_opt,
+};
^ permalink raw reply related
* [PATCH iproute2 v2] GRE over IPv6 tunnel support
From: Dmitry Kozlov @ 2012-07-28 7:52 UTC (permalink / raw)
To: netdev
GRE over IPv6 tunnel support.
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
---
Changes:
Implemented 'ip link' family of commands to manage
ip6gre/ip6gretap tunnels.
include/linux/if_arp.h | 15 ++
include/linux/if_tunnel.h | 21 +++
include/linux/ip6_tunnel.h | 18 ++
ip/Makefile | 2 +-
ip/ip6tunnel.c | 133 +++++++++++++--
ip/link_gre6.c | 397
++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 567
insertions(+), 19 deletions(-)
diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h
index d2de0f9..9adcc29 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -92,6 +92,7 @@
#define ARPHRD_PHONET 820 /* PhoNet media
type */ #define ARPHRD_PHONET_PIPE
821 /* PhoNet pipe header */ #define
ARPHRD_CAIF 822 /* CAIF media
type */ +#define ARPHRD_IP6GRE
823 /* GRE over IPv6 */ #define
ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */ @@
-154,5 +155,19 @@ struct arphdr {
};
+#ifdef __KERNEL__
+#include <linux/skbuff.h>
+
+static inline struct arphdr *arp_hdr(const struct sk_buff *skb)
+{
+ return (struct arphdr *)skb_network_header(skb);
+}
+
+static inline int arp_hdr_len(struct net_device *dev)
+{
+ /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
+ return sizeof(struct arphdr) + (dev->addr_len + sizeof(u32)) *
2; +}
+#endif
#endif /* _LINUX_IF_ARP_H */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
index 8c819b8..8c5035a 100644
--- a/include/linux/if_tunnel.h
+++ b/include/linux/if_tunnel.h
@@ -4,6 +4,10 @@
#include <linux/types.h>
#include <asm/byteorder.h>
+#ifdef __KERNEL__
+#include <linux/ip.h>
+#include <linux/in6.h>
+#endif
#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
@@ -71,9 +75,26 @@ enum {
IFLA_GRE_TTL,
IFLA_GRE_TOS,
IFLA_GRE_PMTUDISC,
+ IFLA_GRE_ENCAP_LIMIT,
+ IFLA_GRE_FLOWINFO,
+ IFLA_GRE_FLAGS,
__IFLA_GRE_MAX,
};
#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
+/* VTI-mode i_flags */
+#define VTI_ISVTI 0x0001
+
+enum {
+ IFLA_VTI_UNSPEC,
+ IFLA_VTI_LINK,
+ IFLA_VTI_IKEY,
+ IFLA_VTI_OKEY,
+ IFLA_VTI_LOCAL,
+ IFLA_VTI_REMOTE,
+ __IFLA_VTI_MAX,
+};
+
+#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
#endif /* _IF_TUNNEL_H_ */
diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h
index bf22b03..1efe2e0 100644
--- a/include/linux/ip6_tunnel.h
+++ b/include/linux/ip6_tunnel.h
@@ -31,4 +31,22 @@ struct ip6_tnl_parm {
struct in6_addr raddr; /* remote tunnel end-point
address */ };
+struct ip6_tnl_parm2 {
+ char name[IFNAMSIZ]; /* name of tunnel device */
+ int link; /* ifindex of underlying L2 interface
*/
+ __u8 proto; /* tunnel protocol */
+ __u8 encap_limit; /* encapsulation limit for tunnel */
+ __u8 hop_limit; /* hop limit for tunnel */
+ __be32 flowinfo; /* traffic class and flowlabel for
tunnel */
+ __u32 flags; /* tunnel flags */
+ struct in6_addr laddr; /* local tunnel end-point
address */
+ struct in6_addr raddr; /* remote tunnel end-point
address */ +
+ __be16 i_flags;
+ __be16 o_flags;
+ __be32 i_key;
+ __be32 o_key;
+};
+
+
#endif
diff --git a/ip/Makefile b/ip/Makefile
index e029ea1..9c54a0b 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -3,7 +3,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o
ipnetns.o \ ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
- iplink_macvlan.o iplink_macvtap.o ipl2tp.o
+ iplink_macvlan.o iplink_macvtap.o ipl2tp.o link_gre6.o
RTMONOBJ=rtmon.o
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
index c9720eb..399a579 100644
--- a/ip/ip6tunnel.c
+++ b/ip/ip6tunnel.c
@@ -48,11 +48,12 @@ static void usage(void) __attribute__((noreturn));
static void usage(void)
{
fprintf(stderr, "Usage: ip -f inet6 tunnel { add | change |
del | show } [ NAME ]\n");
- fprintf(stderr, " [ mode { ip6ip6 | ipip6 | any }
]\n");
+ fprintf(stderr, " [ mode { ip6ip6 | ipip6 | ip6gre |
any } ]\n"); fprintf(stderr, " [ remote ADDR local ADDR ]
[ dev PHYS_DEV ]\n"); fprintf(stderr, " [ encaplimit ELIM
]\n"); fprintf(stderr ," [ hoplimit TTL ] [ tclass TCLASS ]
[ flowlabel FLOWLABEL ]\n"); fprintf(stderr, " [ dscp inherit
]\n");
+ fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ]
[ [i|o]csum ]\n"); fprintf(stderr, "\n");
fprintf(stderr, "Where: NAME := STRING\n");
fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
@@ -60,12 +61,13 @@ static void usage(void)
IPV6_DEFAULT_TNL_ENCAP_LIMIT);
fprintf(stderr, " TTL := 0..255 (default=%d)\n",
DEFAULT_TNL_HOP_LIMIT);
- fprintf(stderr, " TOS := { 0x0..0xff |
inherit }\n");
+ fprintf(stderr, " TCLASS := { 0x0..0xff |
inherit }\n"); fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff |
inherit }\n");
+ fprintf(stderr, " KEY := { DOTTED_QUAD |
NUMBER }\n"); exit(-1);
}
-static void print_tunnel(struct ip6_tnl_parm *p)
+static void print_tunnel(struct ip6_tnl_parm2 *p)
{
char remote[64];
char local[64];
@@ -104,9 +106,29 @@ static void print_tunnel(struct ip6_tnl_parm *p)
if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
printf(" dscp inherit");
+
+ if (p->proto == IPPROTO_GRE) {
+ if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) &&
p->o_key == p->i_key)
+ printf(" key %u", ntohl(p->i_key));
+ else if ((p->i_flags|p->o_flags)&GRE_KEY) {
+ if (p->i_flags&GRE_KEY)
+ printf(" ikey %u ", ntohl(p->i_key));
+ if (p->o_flags&GRE_KEY)
+ printf(" okey %u ", ntohl(p->o_key));
+ }
+
+ if (p->i_flags&GRE_SEQ)
+ printf("%s Drop packets out of sequence.\n",
_SL_);
+ if (p->i_flags&GRE_CSUM)
+ printf("%s Checksum in received packet is
required.", _SL_);
+ if (p->o_flags&GRE_SEQ)
+ printf("%s Sequence packets on output.",
_SL_);
+ if (p->o_flags&GRE_CSUM)
+ printf("%s Checksum output packets.", _SL_);
+ }
}
-static int parse_args(int argc, char **argv, int cmd, struct
ip6_tnl_parm *p) +static int parse_args(int argc, char **argv, int
cmd, struct ip6_tnl_parm2 *p) {
int count = 0;
char medium[IFNAMSIZ];
@@ -124,6 +146,9 @@ static int parse_args(int argc, char **argv, int
cmd, struct ip6_tnl_parm *p) strcmp(*argv, "ipip6") == 0 ||
strcmp(*argv, "ip4ip6") == 0)
p->proto = IPPROTO_IPIP;
+ else if (strcmp(*argv, "ip6gre") == 0 ||
+ strcmp(*argv, "gre/ipv6") == 0)
+ p->proto = IPPROTO_GRE;
else if (strcmp(*argv, "any/ipv6") == 0 ||
strcmp(*argv, "any") == 0)
p->proto = 0;
@@ -199,6 +224,60 @@ static int parse_args(int argc, char **argv, int
cmd, struct ip6_tnl_parm *p) if (strcmp(*argv, "inherit") != 0)
invarg("not inherit", *argv);
p->flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ } else if (strcmp(*argv, "key") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = p->o_key =
get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value
of \"key\"\n");
+ exit(-1);
+ }
+ p->i_key = p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "ikey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->i_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->i_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value
of \"ikey\"\n");
+ exit(-1);
+ }
+ p->i_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "okey") == 0) {
+ unsigned uval;
+ NEXT_ARG();
+ p->o_flags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ p->o_key = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value
of \"okey\"\n");
+ exit(-1);
+ }
+ p->o_key = htonl(uval);
+ }
+ } else if (strcmp(*argv, "seq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "iseq") == 0) {
+ p->i_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "oseq") == 0) {
+ p->o_flags |= GRE_SEQ;
+ } else if (strcmp(*argv, "csum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ p->o_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "icsum") == 0) {
+ p->i_flags |= GRE_CSUM;
+ } else if (strcmp(*argv, "ocsum") == 0) {
+ p->o_flags |= GRE_CSUM;
} else {
if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
@@ -209,7 +288,7 @@ static int parse_args(int argc, char **argv, int
cmd, struct ip6_tnl_parm *p) duparg2("name", *argv);
strncpy(p->name, *argv, IFNAMSIZ - 1);
if (cmd == SIOCCHGTUNNEL && count == 0) {
- struct ip6_tnl_parm old_p;
+ struct ip6_tnl_parm2 old_p;
memset(&old_p, 0, sizeof(old_p));
if (tnl_get_ioctl(*argv, &old_p))
return -1;
@@ -227,7 +306,7 @@ static int parse_args(int argc, char **argv, int
cmd, struct ip6_tnl_parm *p) return 0;
}
-static void ip6_tnl_parm_init(struct ip6_tnl_parm *p, int
apply_default) +static void ip6_tnl_parm_init(struct ip6_tnl_parm2 *p,
int apply_default) {
memset(p, 0, sizeof(*p));
p->proto = IPPROTO_IPV6;
@@ -241,8 +320,8 @@ static void ip6_tnl_parm_init(struct ip6_tnl_parm
*p, int apply_default)
* @p1: user specified parameter
* @p2: database entry
*/
-static int ip6_tnl_parm_match(const struct ip6_tnl_parm *p1,
- const struct ip6_tnl_parm *p2)
+static int ip6_tnl_parm_match(const struct ip6_tnl_parm2 *p1,
+ const struct ip6_tnl_parm2 *p2)
{
return ((!p1->link || p1->link == p2->link) &&
(!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
@@ -260,7 +339,7 @@ static int ip6_tnl_parm_match(const struct
ip6_tnl_parm *p1, (!p1->flags || (p1->flags & p2->flags)));
}
-static int do_tunnels_list(struct ip6_tnl_parm *p)
+static int do_tunnels_list(struct ip6_tnl_parm2 *p)
{
char buf[512];
int err = -1;
@@ -284,7 +363,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
rx_fifo, rx_frame,
tx_bytes, tx_packets, tx_errs, tx_drops,
tx_fifo, tx_colls, tx_carrier, rx_multi;
- struct ip6_tnl_parm p1;
+ struct ip6_tnl_parm2 p1;
char *ptr;
buf[sizeof(buf) - 1] = '\0';
@@ -309,10 +388,12 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
fprintf(stderr, "Failed to get type of
[%s]\n", name); continue;
}
- if (type != ARPHRD_TUNNEL6)
+ if (type != ARPHRD_TUNNEL6 && type != ARPHRD_IP6GRE)
continue;
memset(&p1, 0, sizeof(p1));
ip6_tnl_parm_init(&p1, 0);
+ if (type == ARPHRD_IP6GRE)
+ p1.proto = IPPROTO_GRE;
strcpy(p1.name, name);
p1.link = ll_name_to_index(p1.name);
if (p1.link == 0)
@@ -343,7 +424,7 @@ static int do_tunnels_list(struct ip6_tnl_parm *p)
static int do_show(int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ll_init_map(&rth);
ip6_tnl_parm_init(&p, 0);
@@ -366,28 +447,44 @@ static int do_show(int argc, char **argv)
static int do_add(int cmd, int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, cmd, &p) < 0)
return -1;
- return tnl_add_ioctl(cmd,
- cmd == SIOCCHGTUNNEL && p.name[0] ?
- p.name : "ip6tnl0", p.name, &p);
+ switch (p.proto) {
+ case IPPROTO_IPIP:
+ case IPPROTO_IPV6:
+ return tnl_add_ioctl(cmd, "ip6tnl0", p.name, &p);
+ case IPPROTO_GRE:
+ return tnl_add_ioctl(cmd, "ip6gre0", p.name, &p);
+ default:
+ fprintf(stderr, "cannot determine tunnel mode (ip6ip6,
ipip6 or gre)\n");
+ }
+ return -1;
}
static int do_del(int argc, char **argv)
{
- struct ip6_tnl_parm p;
+ struct ip6_tnl_parm2 p;
ip6_tnl_parm_init(&p, 1);
if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
return -1;
- return tnl_del_ioctl(p.name[0] ? p.name : "ip6tnl0", p.name,
&p);
+ switch (p.proto) {
+ case IPPROTO_IPIP:
+ case IPPROTO_IPV6:
+ return tnl_del_ioctl("ip6tnl0", p.name, &p);
+ case IPPROTO_GRE:
+ return tnl_del_ioctl("ip6gre0", p.name, &p);
+ default:
+ return tnl_del_ioctl(p.name, p.name, &p);
+ }
+ return -1;
}
int do_ip6tunnel(int argc, char **argv)
diff --git a/ip/link_gre6.c b/ip/link_gre6.c
new file mode 100644
index 0000000..3ffd3ce
--- /dev/null
+++ b/ip/link_gre6.c
@@ -0,0 +1,397 @@
+/*
+ * link_gre.c gre driver module
+ *
+ * This program is free software; you can redistribute
it and/or
+ * modify it under the terms of the GNU General Public
License
+ * as published by the Free Software Foundation; either
version
+ * 2 of the License, or (at your option) any later
version.
+ *
+ * Authors: Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ */
+
+#include <string.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+#include "tunnel.h"
+
+#define IP6_FLOWINFO_TCLASS htonl(0x0FF00000)
+#define IP6_FLOWINFO_FLOWLABEL htonl(0x000FFFFF)
+
+#define DEFAULT_TNL_HOP_LIMIT (64)
+
+static void usage(void) __attribute__((noreturn));
+static void usage(void)
+{
+ fprintf(stderr, "Usage: ip link { add | set | change | replace
| del } NAME\n");
+ fprintf(stderr, " type { ip6gre | ip6gretap }
[ remote ADDR ] [ local ADDR ]\n");
+ fprintf(stderr, " [ [i|o]seq ] [ [i|o]key KEY ]
[ [i|o]csum ]\n");
+ fprintf(stderr, " [ hoplimit TTL ] [ encaplimit ELIM
]\n");
+ fprintf(stderr, " [ tclass TCLASS ] [ flowlabel
FLOWLABEL ]\n");
+ fprintf(stderr, " [ dscp inherit ] [ dev PHYS_DEV
]\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Where: NAME := STRING\n");
+ fprintf(stderr, " ADDR := IPV6_ADDRESS\n");
+ fprintf(stderr, " TTL := { 0..255 }
(default=%d)\n",
+ DEFAULT_TNL_HOP_LIMIT);
+ fprintf(stderr, " KEY := { DOTTED_QUAD |
NUMBER }\n");
+ fprintf(stderr, " ELIM := { none |
0..255 }(default=%d)\n",
+ IPV6_DEFAULT_TNL_ENCAP_LIMIT);
+ fprintf(stderr, " FLOWLABEL := { 0x0..0xfffff |
inherit }\n");
+ exit(-1);
+}
+
+static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
+ struct nlmsghdr *n)
+{
+ struct {
+ struct nlmsghdr n;
+ struct ifinfomsg i;
+ char buf[1024];
+ } req;
+ struct ifinfomsg *ifi = (struct ifinfomsg *)(n + 1);
+ struct rtattr *tb[IFLA_MAX + 1];
+ struct rtattr *linkinfo[IFLA_INFO_MAX+1];
+ struct rtattr *greinfo[IFLA_GRE_MAX + 1];
+ __u16 iflags = 0;
+ __u16 oflags = 0;
+ unsigned ikey = 0;
+ unsigned okey = 0;
+ struct in6_addr raddr = IN6ADDR_ANY_INIT;
+ struct in6_addr laddr = IN6ADDR_ANY_INIT;
+ unsigned link = 0;
+ unsigned flowinfo = 0;
+ unsigned flags = 0;
+ __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
+ __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
+ int len;
+
+ if (!(n->nlmsg_flags & NLM_F_CREATE)) {
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETLINK;
+ req.i.ifi_family = preferred_family;
+ req.i.ifi_index = ifi->ifi_index;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n) < 0) {
+get_failed:
+ fprintf(stderr,
+ "Failed to get existing tunnel
info.\n");
+ return -1;
+ }
+
+ len = req.n.nlmsg_len;
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if (len < 0)
+ goto get_failed;
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(&req.i), len);
+
+ if (!tb[IFLA_LINKINFO])
+ goto get_failed;
+
+ parse_rtattr_nested(linkinfo, IFLA_INFO_MAX,
tb[IFLA_LINKINFO]); +
+ if (!linkinfo[IFLA_INFO_DATA])
+ goto get_failed;
+
+ parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
+ linkinfo[IFLA_INFO_DATA]);
+
+ if (greinfo[IFLA_GRE_IKEY])
+ ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
+
+ if (greinfo[IFLA_GRE_OKEY])
+ okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
+
+ if (greinfo[IFLA_GRE_IFLAGS])
+ iflags =
rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]); +
+ if (greinfo[IFLA_GRE_OFLAGS])
+ oflags =
rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]); +
+ if (greinfo[IFLA_GRE_LOCAL])
+ memcpy(&laddr,
RTA_DATA(greinfo[IFLA_GRE_LOCAL]), sizeof(laddr)); +
+ if (greinfo[IFLA_GRE_REMOTE])
+ memcpy(&raddr,
RTA_DATA(greinfo[IFLA_GRE_REMOTE]), sizeof(raddr)); +
+ if (greinfo[IFLA_GRE_TTL])
+ hop_limit =
rta_getattr_u8(greinfo[IFLA_GRE_TTL]); +
+ if (greinfo[IFLA_GRE_LINK])
+ link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
+
+ if (greinfo[IFLA_GRE_ENCAP_LIMIT])
+ encap_limit =
rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]);
+
+ if (greinfo[IFLA_GRE_FLOWINFO])
+ flowinfo =
rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]);
+
+ if (greinfo[IFLA_GRE_FLAGS])
+ flags =
rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
+ }
+
+ while (argc > 0) {
+ if (!matches(*argv, "key")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ iflags |= GRE_KEY;
+ oflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0) < 0)
{
+ fprintf(stderr,
+ "Invalid value for
\"key\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+
+ ikey = okey = uval;
+ } else if (!matches(*argv, "ikey")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ iflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value
of \"ikey\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+ ikey = uval;
+ } else if (!matches(*argv, "okey")) {
+ unsigned uval;
+
+ NEXT_ARG();
+ oflags |= GRE_KEY;
+ if (strchr(*argv, '.'))
+ uval = get_addr32(*argv);
+ else {
+ if (get_unsigned(&uval, *argv, 0)<0) {
+ fprintf(stderr, "invalid value
of \"okey\"\n");
+ exit(-1);
+ }
+ uval = htonl(uval);
+ }
+ okey = uval;
+ } else if (!matches(*argv, "seq")) {
+ iflags |= GRE_SEQ;
+ oflags |= GRE_SEQ;
+ } else if (!matches(*argv, "iseq")) {
+ iflags |= GRE_SEQ;
+ } else if (!matches(*argv, "oseq")) {
+ oflags |= GRE_SEQ;
+ } else if (!matches(*argv, "csum")) {
+ iflags |= GRE_CSUM;
+ oflags |= GRE_CSUM;
+ } else if (!matches(*argv, "icsum")) {
+ iflags |= GRE_CSUM;
+ } else if (!matches(*argv, "ocsum")) {
+ oflags |= GRE_CSUM;
+ } else if (!matches(*argv, "remote")) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_prefix(&addr, *argv, preferred_family);
+ if (addr.family == AF_UNSPEC)
+ invarg("\"remote\" address family is
AF_UNSPEC", *argv);
+ memcpy(&raddr, &addr.data, sizeof(raddr));
+ } else if (!matches(*argv, "local")) {
+ inet_prefix addr;
+ NEXT_ARG();
+ get_prefix(&addr, *argv, preferred_family);
+ if (addr.family == AF_UNSPEC)
+ invarg("\"local\" address family is
AF_UNSPEC", *argv);
+ memcpy(&laddr, &addr.data, sizeof(laddr));
+ } else if (!matches(*argv, "dev")) {
+ NEXT_ARG();
+ link = if_nametoindex(*argv);
+ if (link == 0)
+ exit(-1);
+ } else if (!matches(*argv, "ttl") ||
+ !matches(*argv, "hoplimit")) {
+ __u8 uval;
+ NEXT_ARG();
+ if (get_u8(&uval, *argv, 0))
+ invarg("invalid TTL", *argv);
+ hop_limit = uval;
+ } else if (!matches(*argv, "tos") ||
+ !matches(*argv, "tclass") ||
+ !matches(*argv, "dsfield")) {
+ __u8 uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") == 0)
+ flags |= IP6_TNL_F_USE_ORIG_TCLASS;
+ else {
+ if (get_u8(&uval, *argv, 16))
+ invarg("invalid TClass",
*argv);
+ flowinfo |= htonl((__u32)uval << 20) &
IP6_FLOWINFO_TCLASS;
+ flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
+ }
+ } else if (strcmp(*argv, "flowlabel") == 0 ||
+ strcmp(*argv, "fl") == 0) {
+ __u32 uval;
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") == 0)
+ flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
+ else {
+ if (get_u32(&uval, *argv, 16))
+ invarg("invalid Flowlabel",
*argv);
+ if (uval > 0xFFFFF)
+ invarg("invalid Flowlabel",
*argv);
+ flowinfo |= htonl(uval) &
IP6_FLOWINFO_FLOWLABEL;
+ flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
+ }
+ } else if (strcmp(*argv, "dscp") == 0) {
+ NEXT_ARG();
+ if (strcmp(*argv, "inherit") != 0)
+ invarg("not inherit", *argv);
+ flags |= IP6_TNL_F_RCV_DSCP_COPY;
+ } else
+ usage();
+ argc--; argv++;
+ }
+
+ addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
+ addattr32(n, 1024, IFLA_GRE_OKEY, okey);
+ addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
+ addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
+ addattr_l(n, 1024, IFLA_GRE_LOCAL, &laddr, sizeof(laddr));
+ addattr_l(n, 1024, IFLA_GRE_REMOTE, &raddr, sizeof(raddr));
+ if (link)
+ addattr32(n, 1024, IFLA_GRE_LINK, link);
+ addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
+ addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
+ addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
+ addattr_l(n, 1024, IFLA_GRE_FLAGS, &flowinfo, 4);
+
+ return 0;
+}
+
+static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr
*tb[]) +{
+ char s1[1024];
+ char s2[64];
+ const char *local = "any";
+ const char *remote = "any";
+ unsigned iflags = 0;
+ unsigned oflags = 0;
+ unsigned flags = 0;
+ unsigned flowinfo = 0;
+ struct in6_addr in6_addr_any = IN6ADDR_ANY_INIT;
+
+ if (!tb)
+ return;
+
+ if (tb[IFLA_GRE_FLAGS])
+ flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
+
+ if (tb[IFLA_GRE_FLOWINFO])
+ flags = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
+
+ if (tb[IFLA_GRE_REMOTE]) {
+ struct in6_addr addr;
+ memcpy(&addr, RTA_DATA(tb[IFLA_GRE_REMOTE]),
sizeof(addr)); +
+ if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+ remote = format_host(AF_INET6, sizeof(addr),
&addr, s1, sizeof(s1));
+ }
+
+ fprintf(f, "remote %s ", remote);
+
+ if (tb[IFLA_GRE_LOCAL]) {
+ struct in6_addr addr;
+ memcpy(&addr, RTA_DATA(tb[IFLA_GRE_LOCAL]),
sizeof(addr)); +
+ if (memcmp(&addr, &in6_addr_any, sizeof(addr)))
+ local = format_host(AF_INET6, sizeof(addr),
&addr, s1, sizeof(s1));
+ }
+
+ fprintf(f, "local %s ", local);
+
+ if (tb[IFLA_GRE_LINK] && rta_getattr_u32(tb[IFLA_GRE_LINK])) {
+ unsigned link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
+ const char *n = if_indextoname(link, s2);
+
+ if (n)
+ fprintf(f, "dev %s ", n);
+ else
+ fprintf(f, "dev %u ", link);
+ }
+
+ if (tb[IFLA_GRE_TTL] && rta_getattr_u8(tb[IFLA_GRE_TTL]))
+ fprintf(f, "hoplimit %d ",
rta_getattr_u8(tb[IFLA_GRE_TTL])); +
+ if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
+ fprintf(f, "encaplimit none ");
+ else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
+ int encap_limit =
rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]); +
+ fprintf(f, "encaplimit %d ", encap_limit);
+ }
+
+ if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+ fprintf(f, "flowlabel inherit ");
+ else
+ fprintf(f, "flowlabel 0x%05x ", ntohl(flowinfo &
IP6_FLOWINFO_FLOWLABEL));
+
+ if (flags & IP6_TNL_F_RCV_DSCP_COPY)
+ fprintf(f, "dscp inherit ");
+
+ if (tb[IFLA_GRE_IFLAGS])
+ iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
+
+ if (tb[IFLA_GRE_OFLAGS])
+ oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
+
+ if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
+ inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2,
sizeof(s2));
+ fprintf(f, "ikey %s ", s2);
+ }
+
+ if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
+ inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2,
sizeof(s2));
+ fprintf(f, "okey %s ", s2);
+ }
+
+ if (iflags & GRE_SEQ)
+ fputs("iseq ", f);
+ if (oflags & GRE_SEQ)
+ fputs("oseq ", f);
+ if (iflags & GRE_CSUM)
+ fputs("icsum ", f);
+ if (oflags & GRE_CSUM)
+ fputs("ocsum ", f);
+}
+
+struct link_util ip6gre_link_util = {
+ .id = "ip6gre",
+ .maxattr = IFLA_GRE_MAX,
+ .parse_opt = gre_parse_opt,
+ .print_opt = gre_print_opt,
+};
+
+struct link_util ip6gretap_link_util = {
+ .id = "ip6gretap",
+ .maxattr = IFLA_GRE_MAX,
+ .parse_opt = gre_parse_opt,
+ .print_opt = gre_print_opt,
+};
^ permalink raw reply related
* [GIT] Networking
From: David Miller @ 2012-07-28 7:52 UTC (permalink / raw)
To: torvalds; +Cc: akpm, netdev, linux-kernel
Several bug fixes, some to new features appearing in this merge window,
some that have been around for a while.
I have a short list of known problems that need to be sorted out, but
all of them can be solved easily during the run up to 3.6-final.
I'll be offline until Sunday afternoon, but nothing need hold up
3.6-rc1 and the close of the merge window, networking wise, at this
point.
1) Fix interface check in ipv4 TCP early demux, from Eric Dumazet.
2) Fix a long standing bug in TCP DMA to userspace offload that can
hang applications using MSG_TRUNC, from Jiri Kosina.
3) Don't allow TCP_USER_TIMEOUT to be negative, from Hangbin Liu.
4) Don't use GFP_KERNEL under spinlock in kaweth driver, from Dan
Carpenter
Please pull, thanks a lot.
The following changes since commit b387e41e523c1aa347cff055455d0dd129357df4:
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc (2012-07-27 08:35:26 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git master
for you to fetch changes up to 7b9b04fb728ec0b94464ed902f3395aa592c5bcf:
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless (2012-07-27 14:25:40 -0700)
----------------------------------------------------------------
Dan Carpenter (1):
USB: kaweth.c: use GFP_ATOMIC under spin_lock
David S. Miller (1):
Merge branch 'for-davem' of git://git.kernel.org/.../linville/wireless
Eric Dumazet (1):
ipv4: fix TCP early demux
Hangbin Liu (1):
tcp: Add TCP_USER_TIMEOUT negative value check
Hauke Mehrtens (2):
bcma: fix regression in interrupt assignment on mips
bcma: add missing iounmap on error path
Jesse Gross (1):
Revert "openvswitch: potential NULL deref in sample()"
Jiri Benc (1):
net: fix rtnetlink IFF_PROMISC and IFF_ALLMULTI handling
Jiri Kosina (1):
tcp: perform DMA to userspace only if there is a task waiting for it
John W. Linville (1):
Merge branch 'master' of git://git.kernel.org/.../linville/wireless into for-davem
Thomas Huehn (1):
mac80211_hwsim: fix possible race condition in usage of info->control.sta & control.vif
drivers/bcma/driver_mips.c | 6 +++---
drivers/bcma/scan.c | 15 ++++++++++-----
drivers/net/usb/kaweth.c | 2 +-
drivers/net/wireless/mac80211_hwsim.c | 5 -----
net/core/rtnetlink.c | 8 +++++++-
net/ipv4/tcp.c | 5 ++++-
net/ipv4/tcp_input.c | 5 ++++-
net/ipv4/tcp_ipv4.c | 14 ++++++--------
net/ipv4/tcp_minisocks.c | 1 +
net/openvswitch/actions.c | 3 ---
10 files changed, 36 insertions(+), 28 deletions(-)
^ permalink raw reply
* [PATCH v2] dynamic_debug: Restore dev_dbg functionality, optimize stack
From: Joe Perches @ 2012-07-28 7:55 UTC (permalink / raw)
To: Andrew Morton, Greg Kroah-Hartman, David S. Miller, Jason Baron
Cc: Jim Cromie, Kay Sievers, linux-kernel, netdev
In-Reply-To: <1343334310.17538.32.camel@joe2Laptop>
commit c4e00daaa9 ("driver-core: extend dev_printk() to pass structured data")
changed __dev_printk and broke dynamic-debug's ability to control the
dynamic prefix of dev_dbg(dev,..).
dynamic_emit_prefix() adds "[tid] module:func:line:" to the output and
those additions got lost.
In addition, the current dynamic debug code uses up to 3 recursion
levels via %pV. This can consume quite a bit of stack. Directly
call printk_emit to reduce the recursion depth.
These changes include:
o Remove KERN_DEBUG from dynamic_emit_prefix
o Create and use function create_syslog_header to format the syslog
header for printk_emit uses.
o Call create_syslog_header and neaten __dev_printk
o Call create_syslog_header and printk_emit from dynamic_dev_dbg
o Call create_syslog_header and printk_emit from dynamic_netdev_dbg
o Make __dev_printk and __netdev_printk static not global
o Remove include header declarations of __dev_printk and __netdev_printk
o Remove now unused EXPORT_SYMBOL()s of __dev_printk and __netdev_printk
o Whitespace neatening
Changes in v2:
o Fix dynamic_emit_prefix to always initialize output
o Call create_syslog_header and emit_printk from__netdev_printk and
eliminate call to dev_printk to remove another recursion via %pV
Signed-off-by: Joe Perches <joe@perches.com>
---
drivers/base/core.c | 57 +++++++++++++++++++++++----------------
include/linux/device.h | 11 +++-----
include/linux/netdevice.h | 3 --
lib/dynamic_debug.c | 65 ++++++++++++++++++++++++++++++++++++---------
net/core/dev.c | 24 ++++++++++++-----
5 files changed, 106 insertions(+), 54 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f338037..d46b635 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1861,25 +1861,19 @@ void device_shutdown(void)
*/
#ifdef CONFIG_PRINTK
-int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
+int create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
{
- char dict[128];
- size_t dictlen = 0;
const char *subsys;
-
- if (!dev)
- return printk("%s(NULL device *): %pV", level, vaf);
+ size_t pos = 0;
if (dev->class)
subsys = dev->class->name;
else if (dev->bus)
subsys = dev->bus->name;
else
- goto skip;
+ return 0;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "SUBSYSTEM=%s", subsys);
+ pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
/*
* Add device identifier DEVICE=:
@@ -1895,28 +1889,41 @@ int __dev_printk(const char *level, const struct device *dev,
c = 'b';
else
c = 'c';
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=%c%u:%u",
- c, MAJOR(dev->devt), MINOR(dev->devt));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=%c%u:%u",
+ c, MAJOR(dev->devt), MINOR(dev->devt));
} else if (strcmp(subsys, "net") == 0) {
struct net_device *net = to_net_dev(dev);
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=n%u", net->ifindex);
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=n%u", net->ifindex);
} else {
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=+%s:%s", subsys, dev_name(dev));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=+%s:%s", subsys, dev_name(dev));
}
-skip:
- return printk_emit(0, level[1] - '0',
- dictlen ? dict : NULL, dictlen,
+
+ return pos;
+}
+EXPORT_SYMBOL(create_syslog_header);
+
+static int __dev_printk(const char *level, const struct device *dev,
+ struct va_format *vaf)
+{
+ char hdr[128];
+ size_t hdrlen;
+
+ if (!dev)
+ return printk("%s(NULL device *): %pV", level, vaf);
+
+ hdrlen = create_syslog_header(dev, hdr, sizeof(hdr));
+
+ return printk_emit(0, level[1] - '0', hdrlen ? hdr : NULL, hdrlen,
"%s %s: %pV",
dev_driver_string(dev), dev_name(dev), vaf);
}
-EXPORT_SYMBOL(__dev_printk);
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
@@ -1931,6 +1938,7 @@ int dev_printk(const char *level, const struct device *dev,
vaf.va = &args;
r = __dev_printk(level, dev, &vaf);
+
va_end(args);
return r;
@@ -1950,6 +1958,7 @@ int func(const struct device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __dev_printk(kern_level, dev, &vaf); \
+ \
va_end(args); \
\
return r; \
diff --git a/include/linux/device.h b/include/linux/device.h
index 52a5f15..89b246c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -891,12 +891,12 @@ extern const char *dev_driver_string(const struct device *dev);
#ifdef CONFIG_PRINTK
-extern int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf);
+extern int create_syslog_header(const struct device *dev,
+ char *hdr, size_t hdrlen);
+
extern __printf(3, 4)
int dev_printk(const char *level, const struct device *dev,
- const char *fmt, ...)
- ;
+ const char *fmt, ...);
extern __printf(2, 3)
int dev_emerg(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
@@ -914,9 +914,6 @@ int _dev_info(const struct device *dev, const char *fmt, ...);
#else
-static inline int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
-{ return 0; }
static inline __printf(3, 4)
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eb06e58..291e0ee 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2715,9 +2715,6 @@ static inline const char *netdev_name(const struct net_device *dev)
return dev->name;
}
-extern int __netdev_printk(const char *level, const struct net_device *dev,
- struct va_format *vaf);
-
extern __printf(3, 4)
int netdev_printk(const char *level, const struct net_device *dev,
const char *format, ...);
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 7ca29a0..6b3ebab 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -521,25 +521,25 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
int pos_after_tid;
int pos = 0;
- pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG);
+ *buf = '\0';
+
if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
if (in_interrupt())
- pos += snprintf(buf + pos, remaining(pos), "%s ",
- "<intr>");
+ pos += snprintf(buf + pos, remaining(pos), "<intr> ");
else
pos += snprintf(buf + pos, remaining(pos), "[%d] ",
- task_pid_vnr(current));
+ task_pid_vnr(current));
}
pos_after_tid = pos;
if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
- desc->modname);
+ desc->modname);
if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
- desc->function);
+ desc->function);
if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
pos += snprintf(buf + pos, remaining(pos), "%d:",
- desc->lineno);
+ desc->lineno);
if (pos - pos_after_tid)
pos += snprintf(buf + pos, remaining(pos), " ");
if (pos >= PREFIX_SIZE)
@@ -559,9 +559,13 @@ int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);
+
+ res = printk(KERN_DEBUG "%s%pV",
+ dynamic_emit_prefix(descriptor, buf), &vaf);
+
va_end(args);
return res;
@@ -574,15 +578,30 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor,
struct va_format vaf;
va_list args;
int res;
- char buf[PREFIX_SIZE];
BUG_ON(!descriptor);
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf);
+
+ if (!dev) {
+ res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
+ } else {
+ char buf[PREFIX_SIZE];
+ char dict[128];
+ size_t dictlen;
+
+ dictlen = create_syslog_header(dev, dict, sizeof(dict));
+
+ res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen,
+ "%s%s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev), dev_name(dev), &vaf);
+ }
+
va_end(args);
return res;
@@ -592,20 +611,40 @@ EXPORT_SYMBOL(__dynamic_dev_dbg);
#ifdef CONFIG_NET
int __dynamic_netdev_dbg(struct _ddebug *descriptor,
- const struct net_device *dev, const char *fmt, ...)
+ const struct net_device *dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int res;
- char buf[PREFIX_SIZE];
BUG_ON(!descriptor);
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = __netdev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf);
+
+ if (dev && dev->dev.parent) {
+ char buf[PREFIX_SIZE];
+ char dict[128];
+ size_t dictlen;
+
+ dictlen = create_syslog_header(dev->dev.parent,
+ dict, sizeof(dict));
+
+ res = printk_emit(0, 7, dictlen ? dict : NULL, dictlen,
+ "%s%s %s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev->dev.parent),
+ dev_name(dev->dev.parent),
+ netdev_name(dev), &vaf);
+ } else if (dev) {
+ res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf);
+ } else {
+ res = printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
+ }
+
va_end(args);
return res;
diff --git a/net/core/dev.c b/net/core/dev.c
index 0ebaea1..c7bcea4 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6358,22 +6358,30 @@ const char *netdev_drivername(const struct net_device *dev)
return empty;
}
-int __netdev_printk(const char *level, const struct net_device *dev,
+static int __netdev_printk(const char *level, const struct net_device *dev,
struct va_format *vaf)
{
int r;
- if (dev && dev->dev.parent)
- r = dev_printk(level, dev->dev.parent, "%s: %pV",
- netdev_name(dev), vaf);
- else if (dev)
+ if (dev && dev->dev.parent) {
+ char dict[128];
+ size_t dictlen = create_syslog_header(dev->dev.parent,
+ dict, sizeof(dict));
+
+ r = printk_emit(0, level[1] - '0',
+ dictlen ? dict : NULL, dictlen,
+ "%s %s %s: %pV",
+ dev_driver_string(dev->dev.parent),
+ dev_name(dev->dev.parent),
+ netdev_name(dev), &vaf);
+ } else if (dev) {
r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
- else
+ } else {
r = printk("%s(NULL net_device): %pV", level, vaf);
+ }
return r;
}
-EXPORT_SYMBOL(__netdev_printk);
int netdev_printk(const char *level, const struct net_device *dev,
const char *format, ...)
@@ -6388,6 +6396,7 @@ int netdev_printk(const char *level, const struct net_device *dev,
vaf.va = &args;
r = __netdev_printk(level, dev, &vaf);
+
va_end(args);
return r;
@@ -6407,6 +6416,7 @@ int func(const struct net_device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __netdev_printk(level, dev, &vaf); \
+ \
va_end(args); \
\
return r; \
--
1.7.8.111.gad25c.dirty
^ permalink raw reply related
* Re: [PATCH iproute2 v2] GRE over IPv6 tunnel support
From: David Miller @ 2012-07-28 7:58 UTC (permalink / raw)
To: xeb; +Cc: netdev
In-Reply-To: <20120728115226.66da9d77@comp1>
From: Dmitry Kozlov <xeb@mail.ru>
Date: Sat, 28 Jul 2012 11:52:26 +0400
> GRE over IPv6 tunnel support.
>
> Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
> ---
> Changes:
> Implemented 'ip link' family of commands to manage
> ip6gre/ip6gretap tunnels.
Can you please stop sending your patches multiple times?
This is the second time you've done this today.
It makes extra work for us, because we have to keep blowing away the
spurious entries you keep adding to our automated patch queue at:
http://patchwork.ozlabs.org/project/netdev/list/
Thanks.
^ permalink raw reply
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