* [RESEND][XFRM][PATCH v4] Fix unexpected SA hard expiration after setting new date
From: Fan Du @ 2012-07-27 2:39 UTC (permalink / raw)
To: davem, herbert; +Cc: netdev
Hi, Dave
It has been more than one month since my last post of this patch.
Previous posting receives no objection from you and Herbert, so I
rebase it with latest linux-3.5, then it can be easily picked up.
I'm wondering could you please take a look at this patch ?
Any comments are really welcome!
Thanks
Changelog:
v1->v2
1) use xflags instead of creating new flags(suggested by Steffen Klassert)
v2->v3
1) fix email problem, and remove cc to myself(requested by David Miller)
v3->v4
1) fix typo when clearing XFRM_SOFT_EXPIRE(thanks for David Miller)
2) fix email problem, and remove cc to myself AGAIN!!!
*Background*:
Once IPsec SAs are created between two peers, kernel setup a timer to monitor
two events: soft/hard expiration. However the timer handler use xtime to
caculate whether it's soft or hard expiration event.
normal code flow(hard expire time:100s, soft expire time:82s)
a) When new SAs created, xfrm_timer_handler is called one second
after its creation. At this point, calculate soft expire
interval(81s), setup the timer;
b) soft expire occur, rearm the timer with hard expire interval(18s)
then notify racoon2 about soft expire event. racoon2 will create
new SAs.
c) hard expire happen, notify racoon2 about it. racoon2 will delete
the old SAs.
*Scenario*:
Setting a new date before b),and after a) could result c) happens first,
As a result, old SAs is deleted before new ones are created. Normally
new SAs will be created by the next time networking traffic, but there
is a small time being when networking connection is down, this could
result in upper layer connections failed in tel comm area, thus it's
better to keep it strict in sequence.
*Workaround*:
set new time could happen:
1) before a), then SAs is updated with new time.
2) before b),and after a)
2a) When new SAs created, xfrm_timer_handler is called one second
after its creation. At this point, calculate soft expire
interval(81s), setup the timer;(set flag to mark next time should
be soft time expire)
<<---- new date comes
2b) soft expire occur, the calculation results in a hard time expire
event, but flag is set, so catch ya. Sync the addtime, and rearm
the timer with hard expire interval(18s), then notify racoon2
about soft expire event;
2c) hard expire happen, notify racoon2 about it;
so everything is in order.
3) after b), hard expire always happened anyway.
^ permalink raw reply
* [PATCH] [XFRM] Fix unexpected SA hard expiration after changing date
From: Fan Du @ 2012-07-27 2:39 UTC (permalink / raw)
To: davem, herbert; +Cc: netdev
In-Reply-To: <1343356759-24767-1-git-send-email-fdu@windriver.com>
After SA is setup, one timer is armed to detect soft/hard expiration,
however the timer handler uses xtime to do the math. This makes hard
expiration occurs first before soft expiration after setting new date
with big interval. As a result new child SA is deleted before rekeying
the new one.
Signed-off-by: Fan Du <fdu@windriver.com>
---
include/net/xfrm.h | 4 ++++
net/xfrm/xfrm_state.c | 22 ++++++++++++++++++----
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index d9509eb..62b619e 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -213,6 +213,9 @@ struct xfrm_state {
struct xfrm_lifetime_cur curlft;
struct tasklet_hrtimer mtimer;
+ /* used to fix curlft->add_time when changing date */
+ long saved_tmo;
+
/* Last used time */
unsigned long lastused;
@@ -238,6 +241,7 @@ static inline struct net *xs_net(struct xfrm_state *x)
/* xflags - make enum if more show up */
#define XFRM_TIME_DEFER 1
+#define XFRM_SOFT_EXPIRE 2
enum {
XFRM_STATE_VOID,
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5b228f9..fb64dc6 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -415,8 +415,18 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
if (x->lft.hard_add_expires_seconds) {
long tmo = x->lft.hard_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
- goto expired;
+ if (tmo <= 0) {
+ if (x->xflags & XFRM_SOFT_EXPIRE) {
+ /* enter hard expire without soft expire first?!
+ * setting a new date could trigger this.
+ * workarbound: fix x->curflt.add_time by below:
+ */
+ x->curlft.add_time = now - x->saved_tmo - 1;
+ tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
+
+ } else
+ goto expired;
+ }
if (tmo < next)
next = tmo;
}
@@ -433,10 +443,14 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
if (x->lft.soft_add_expires_seconds) {
long tmo = x->lft.soft_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
+ if (tmo <= 0) {
warn = 1;
- else if (tmo < next)
+ x->xflags &= ~XFRM_SOFT_EXPIRE;
+ } else if (tmo < next) {
next = tmo;
+ x->xflags |= XFRM_SOFT_EXPIRE;
+ x->saved_tmo = tmo;
+ }
}
if (x->lft.soft_use_expires_seconds) {
long tmo = x->lft.soft_use_expires_seconds +
--
1.7.1
^ permalink raw reply related
* Re: [PATCH 00/16] Remove the ipv4 routing cache
From: David Miller @ 2012-07-27 3:08 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
In-Reply-To: <CAKgT0UdhqDQSFZkWw9w8L2qsCS+MM_DLQE+Vq0mKcYZmxhs=8Q@mail.gmail.com>
From: Alexander Duyck <alexander.duyck@gmail.com>
Date: Thu, 26 Jul 2012 19:14:55 -0700
> Any idea why these look-ups are so expensive in the first place? When
> I dump fib_trie it doesn't look like I have much there. I would have
> thought the table would be pretty static with just 8 flows all going
> to the same destination address, but it seems like I was getting hit
> with cache misses for some reason.
A lot of the overhead comes from write traffic that results from
filling in the "fib_result" structure onto the callers stack.
The return value from a fib_lookup() has far too many components. But
simplifying things is not easy.
^ permalink raw reply
* Re: [PATCH] r8169: revert "add byte queue limit support".
From: Stefan Lippers-Hollmann @ 2012-07-27 3:36 UTC (permalink / raw)
To: Francois Romieu
Cc: Eric Dumazet, David Miller, hayeswang, netdev,
Alex Villacís Lasso, Josh Boyer, Tom Herbert
In-Reply-To: <20120724053811.GA12053@electric-eye.fr.zoreil.com>
Hi
On Tuesday 24 July 2012, Francois Romieu wrote:
> Eric Dumazet <eric.dumazet@gmail.com> :
> > On Mon, 2012-07-23 at 22:55 +0200, Francois Romieu wrote:
> > > This reverts commit 036dafa28da1e2565a8529de2ae663c37b7a0060.
> [...]
> > bisection is not always the right way to qualify a problem.
>
> I know. At some point I switch from "I could search more" to "users situation
> will improve in a definite timeframe".
>
> > BQL in itself had some fixes coming _after_ commit 036dafa28da1e2565
>
> Thanks.
>
> They are in stable as of 3.4.5:
[…]
> I have obviously not directed users at them and I do not see any
> of the victims using a non -stable / -vendor or recent enough
> kernel to test this patch since the issue has been reported.
>
> They are both worth testing.
[…]
3.4.x up to and including 3.4.4 exposed the problem on these cards[1]:
r8169 0000:04:00.0: eth0: RTL8168d/8111d at 0xffffc90000c72000, 00:24:1d:72:7c:75, XID 081000c0 IRQ 44
r8169 0000:05:00.0: eth1: RTL8168d/8111d at 0xffffc90000c70000, 00:24:1d:72:7c:77, XID 081000c0 IRQ 45
while it is stable with "add byte queue limit support" reverted; 3.4.5+
was only tested with 036dafa28da1e2565a8529de2ae663c37b7a0060 reverted.
Now testing plain 3.5.0, which still includes it, has been reliable for
almost 3 days - while the issue usually triggered within one hour (3
hours at most) in 3.4.[0-4]. It might be a little too early to give a
definitive answer, but so far r8169/ 3.5.0 looks positive.
Regards
Stefan Lippers-Hollmann
[1] Message-Id: <201206290131.49150.s.L-H@gmx.de>
http://lkml.kernel.org/r/<201206290131.49150.s.L-H@gmx.de>
^ permalink raw reply
* Re: [PATCH] iproute: Add route showdump command
From: Pavel Emelyanov @ 2012-07-27 4:14 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Dan Smith, Linux Netdev List
In-Reply-To: <20120726090315.4a566a46@nehalam.linuxnetplumber.net>
On 07/26/2012 08:03 PM, Stephen Hemminger wrote:
> On Thu, 26 Jul 2012 12:53:39 +0400
> Pavel Emelyanov <xemul@parallels.com> wrote:
>
>> Some time ago the save+restore commands were added to ip route (git
>> id f4ff11e3, Add ip route save/restore). These two save the raw rtnl
>> stream into a file and restore one (reading it from stdin).
>>
>> The problem is that there's no way to get the contents of the dump
>> file in a human readable form. How about adding a command that reads
>> the rtnl stream from stdin and prints the data in a way the usual
>> "ip route list" does?
>>
>> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
>
> Being able to decode a dump is great idea.
> Are the user's smart enough not to try it out at the command prompt
> and get totally lost? Maybe another isatty() check is needed.
>
> Another solution would be to put a small header on the save file with
> a magic number that could be checked. This would mean changing save/restore/showdump
> and ideally updating the magic file in distributions.
> .
Hm... The idea with magic looks reasonable.
I will look at what can be done here, thanks!
^ permalink raw reply
* Re: [PATCH RESEND net-next V2] IB/ipoib: break linkage to neighbouring system
From: Or Gerlitz @ 2012-07-27 4:42 UTC (permalink / raw)
To: Roland Dreier; +Cc: David Miller, ogerlitz, netdev, eric.dumazet, cl, shlomop
In-Reply-To: <CAG4TOxNrywFPj_91RzkPikPoWP4hwe+2oninuF7zkrBFo6Q3Kg@mail.gmail.com>
On Wed, Jul 25, 2012 at 10:52 AM, Roland Dreier <roland@kernel.org> wrote:
> On Tue, Jul 24, 2012 at 2:00 PM, David Miller <davem@davemloft.net> wrote:
>> Looks good, Roland you got this?
> Yes, I'll read it over and pick it up, thanks.
Hi Roland,
Just to make sure, you are picking this for 3.6, correct?
I'd like to make sure that the merge of this patch through the
infiniband tree will not introduce dependancy with the review/merge of
the Ethernet IPoIB driver that runs through netdev. If this patch
lands in 3.6-rcX, Dave will eventually pick it into net-next and we're
ok, if not, things get complicated without any real reason, makes
sense?
Or.
^ permalink raw reply
* [PATCH 1/2] iproute: Add magic cookie to route dump file
From: Pavel Emelyanov @ 2012-07-27 4:55 UTC (permalink / raw)
To: Stephen Hemminger, Linux Netdev List
In order to somehow verify that a blob contains route dump a
4-bytes magic is put at the head of the data and is checked
on restore.
Magic digits are taken from Portland (OR) coordinates :) Is
there any more reliable way of generating such?
Signed-of-by: Pavel Emelyanov <xemul@parallels.com>
---
ip/iproute.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/ip/iproute.c b/ip/iproute.c
index 5cd313e..bbb3923 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -1064,6 +1064,8 @@ static int iproute_flush_cache(void)
return 0;
}
+static __u32 route_dump_magic = 0x45311224;
+
int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
{
int ret;
@@ -1072,11 +1074,6 @@ int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
struct rtattr *tb[RTA_MAX+1];
int host_len = -1;
- if (isatty(STDOUT_FILENO)) {
- fprintf(stderr, "Not sending binary stream to stdout\n");
- return -1;
- }
-
host_len = calc_host_len(r);
len -= NLMSG_LENGTH(sizeof(*r));
parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
@@ -1093,6 +1090,24 @@ int save_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
return ret == n->nlmsg_len ? 0 : ret;
}
+static int save_route_prep(void)
+{
+ int ret;
+
+ if (isatty(STDOUT_FILENO)) {
+ fprintf(stderr, "Not sending binary stream to stdout\n");
+ return -1;
+ }
+
+ ret = write(STDOUT_FILENO, &route_dump_magic, sizeof(route_dump_magic));
+ if (ret != sizeof(route_dump_magic)) {
+ fprintf(stderr, "Can't write magic to dump file\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static int iproute_list_flush_or_save(int argc, char **argv, int action)
{
int do_ipv6 = preferred_family;
@@ -1101,9 +1116,12 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
unsigned int mark = 0;
rtnl_filter_t filter_fn;
- if (action == IPROUTE_SAVE)
+ if (action == IPROUTE_SAVE) {
+ if (save_route_prep())
+ return -1;
+
filter_fn = save_route;
- else
+ } else
filter_fn = print_route;
iproute_reset_filter();
@@ -1521,8 +1539,30 @@ int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
return ret;
}
+static int route_dump_check_magic(void)
+{
+ int ret;
+ __u32 magic = 0;
+
+ if (isatty(STDIN_FILENO)) {
+ fprintf(stderr, "Can't restore route dump from a terminal\n");
+ return -1;
+ }
+
+ ret = fread(&magic, sizeof(magic), 1, stdin);
+ if (magic != route_dump_magic) {
+ fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic);
+ return -1;
+ }
+
+ return 0;
+}
+
int iproute_restore(void)
{
+ if (route_dump_check_magic())
+ exit(-1);
+
exit(rtnl_from_file(stdin, &restore_handler, NULL));
}
--
1.5.5.6
^ permalink raw reply related
* [PATCH 2/2] iproute: Add route showdump command (v2)
From: Pavel Emelyanov @ 2012-07-27 4:57 UTC (permalink / raw)
To: Stephen Hemminger, Linux Netdev List
In-Reply-To: <50121F4D.8090606@parallels.com>
Some time ago the save+restore commands were added to ip route (git
id f4ff11e3, Add ip route save/restore). These two save the raw rtnl
stream into a file and restore one (reading it from stdin).
The problem is that there's no way to get the contents of the dump
file in a human readable form. The proposal is to add a command that
reads the rtnl stream from stdin and prints the data in a way the
usual "ip route list" does?
changes since v1:
* Take the magic at the beginning of the dump file into account
* Check for stdin (the dump is taken from) is not a tty
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
---
ip/iproute.c | 17 +++++++++++++++++
1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/ip/iproute.c b/ip/iproute.c
index bbb3923..572e2e8 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -60,6 +60,7 @@ static void usage(void)
fprintf(stderr, "Usage: ip route { list | flush } SELECTOR\n");
fprintf(stderr, " ip route save SELECTOR\n");
fprintf(stderr, " ip route restore\n");
+ fprintf(stderr, " ip route showdump\n");
fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n");
fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n");
fprintf(stderr, " [ mark NUMBER ]\n");
@@ -1566,6 +1567,20 @@ int iproute_restore(void)
exit(rtnl_from_file(stdin, &restore_handler, NULL));
}
+static int show_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+{
+ print_route(nl, n, stdout);
+ return 0;
+}
+
+static int iproute_showdump(void)
+{
+ if (route_dump_check_magic())
+ exit(-1);
+
+ exit(rtnl_from_file(stdin, &show_handler, NULL));
+}
+
void iproute_reset_filter()
{
memset(&filter, 0, sizeof(filter));
@@ -1610,6 +1625,8 @@ int do_iproute(int argc, char **argv)
return iproute_list_flush_or_save(argc-1, argv+1, IPROUTE_SAVE);
if (matches(*argv, "restore") == 0)
return iproute_restore();
+ if (matches(*argv, "showdump") == 0)
+ return iproute_showdump();
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
--
1.5.5.6
^ permalink raw reply related
* Re: [PATCH 2/2] iproute: Add route showdump command (v2)
From: Pavel Emelyanov @ 2012-07-27 5:03 UTC (permalink / raw)
To: Stephen Hemminger, Linux Netdev List
In-Reply-To: <50121FB0.6060200@parallels.com>
Stephen, I have a question regarding the save and restore in iproute tool.
As you know, the save/restore pair for routes works in a very simple and
elegant manner -- on save the raw kernel rtnl stream is just put into a
file, on restore the _very_ _same_ messages are pushed back to the kernel.
Is the same trick possible to save and restore the addresses (the ip
addr command) as well? Or the RTM_GETADDR messages cannot be just read
from and written back to the network stack in a generic case?
Thanks,
Pavel
^ permalink raw reply
* Re: [PATCH] r8169: revert "add byte queue limit support".
From: Eric Dumazet @ 2012-07-27 5:27 UTC (permalink / raw)
To: Stefan Lippers-Hollmann
Cc: Francois Romieu, David Miller, hayeswang, netdev,
Alex Villacís Lasso, Josh Boyer, Tom Herbert
In-Reply-To: <201207270536.50292.s.L-H@gmx.de>
On Fri, 2012-07-27 at 05:36 +0200, Stefan Lippers-Hollmann wrote:
> Hi
>
> On Tuesday 24 July 2012, Francois Romieu wrote:
> > Eric Dumazet <eric.dumazet@gmail.com> :
> > > On Mon, 2012-07-23 at 22:55 +0200, Francois Romieu wrote:
> > > > This reverts commit 036dafa28da1e2565a8529de2ae663c37b7a0060.
> > [...]
> > > bisection is not always the right way to qualify a problem.
> >
> > I know. At some point I switch from "I could search more" to "users situation
> > will improve in a definite timeframe".
> >
> > > BQL in itself had some fixes coming _after_ commit 036dafa28da1e2565
> >
> > Thanks.
> >
> > They are in stable as of 3.4.5:
> […]
> > I have obviously not directed users at them and I do not see any
> > of the victims using a non -stable / -vendor or recent enough
> > kernel to test this patch since the issue has been reported.
> >
> > They are both worth testing.
> […]
>
> 3.4.x up to and including 3.4.4 exposed the problem on these cards[1]:
>
> r8169 0000:04:00.0: eth0: RTL8168d/8111d at 0xffffc90000c72000, 00:24:1d:72:7c:75, XID 081000c0 IRQ 44
> r8169 0000:05:00.0: eth1: RTL8168d/8111d at 0xffffc90000c70000, 00:24:1d:72:7c:77, XID 081000c0 IRQ 45
>
> while it is stable with "add byte queue limit support" reverted; 3.4.5+
> was only tested with 036dafa28da1e2565a8529de2ae663c37b7a0060 reverted.
>
> Now testing plain 3.5.0, which still includes it, has been reliable for
> almost 3 days - while the issue usually triggered within one hour (3
> hours at most) in 3.4.[0-4]. It might be a little too early to give a
> definitive answer, but so far r8169/ 3.5.0 looks positive.
>
> Regards
> Stefan Lippers-Hollmann
>
> [1] Message-Id: <201206290131.49150.s.L-H@gmx.de>
> http://lkml.kernel.org/r/<201206290131.49150.s.L-H@gmx.de>
Thats real good news, thanks !
^ permalink raw reply
* Re: [PATCH 00/16] Remove the ipv4 routing cache
From: David Miller @ 2012-07-27 6:02 UTC (permalink / raw)
To: alexander.duyck; +Cc: eric.dumazet, netdev
In-Reply-To: <20120726.200846.66786272076299783.davem@davemloft.net>
From: David Miller <davem@davemloft.net>
Date: Thu, 26 Jul 2012 20:08:46 -0700 (PDT)
> A lot of the overhead comes from write traffic that results from
> filling in the "fib_result" structure onto the callers stack.
Here's the longer analysis of how things are now.
There are several components to a route lookup result, and struct
fib_result tries to encapsulate all of this.
Another aspect is that our route tables are broken up into different
datas tructures which reference each other, in order to save space.
So the actual objects in the FIB trie are fib_alias structures, and
those point to fib_info. There is a many to one relationship between
FIB trie nodes and fib_info objects.
The idea is that many routes have the same set of nexthops, metrics,
preferred source address, etc.
So one thing we return in the fib_result is a pointer to the fib_info
and an index into the nexthop array (nh_sel). That's why we have all
of these funny accessor's FIB_RES_X(res) which essentially provide
res.fi->fib_nh[res.nh_sel].X
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.
It seems also that the res->scope value can be cribbed from the
fib_info as well.
res->type is embedded in the fib_alias we select hanging off of the
FIB trie node. And the res->prefixlen is taken from the FIB trie
node.
res->tclassid is problematic, because it comes from the FIB rules
tables rather than the FIB trie. We used to store a full FIB rules
pointer in the fib_result, but I reduced it down to just the u32
tclassid.
This whole area, as well as the FIB trie lookup itself, is an area
ripe for a large number of small micro-optimizations that in the end
make it's overhead much more reasonable.
Another thing I haven't mentioned is that another part of FIB trie's
overhead is that it does backtracking. The shorter prefixes sit at
the top of the trie, so when it traverses down it does so until it
can't get a match, then it walks back up to the root until it does
have a match.
^ permalink raw reply
* Re: [PATCH 2/2] iproute: Add route showdump command (v2)
From: Pavel Emelyanov @ 2012-07-27 6:06 UTC (permalink / raw)
To: Stephen Hemminger, Linux Netdev List
In-Reply-To: <5012211A.4040307@parallels.com>
On 07/27/2012 09:03 AM, Pavel Emelyanov wrote:
> As you know, the save/restore pair for routes works in a very simple and
> elegant manner -- on save the raw kernel rtnl stream is just put into a
> file, on restore the _very_ _same_ messages are pushed back to the kernel.
>
> Is the same trick possible to save and restore the addresses (the ip
> addr command) as well? Or the RTM_GETADDR messages cannot be just read
> from and written back to the network stack in a generic case?
I mean something like the below, just put the RTM_GETADDR stream into a
file and push it back into the kernel as is. It worked for me in trivial
cases, but is this approach correct generically?
Log:
iproute: Add ability to save, restore and show the interfaces' addresses
Implementation is similar to the ip route {save|restore|showdump} one.
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
---
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 37deda5..6788544 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -34,6 +34,11 @@
#include "ll_map.h"
#include "ip_common.h"
+enum {
+ IPADD_LIST,
+ IPADD_FLUSH,
+ IPADD_SAVE,
+};
static struct
{
@@ -65,8 +70,9 @@ static void usage(void)
fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n");
fprintf(stderr, " [ CONFFLAG-LIST ]\n");
fprintf(stderr, " ip addr del IFADDR dev STRING\n");
- fprintf(stderr, " ip addr {show|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
+ fprintf(stderr, " ip addr {show|save|flush} [ dev STRING ] [ scope SCOPE-ID ]\n");
fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ]\n");
+ fprintf(stderr, " ip addr {showdump|restore}\n");
fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n");
fprintf(stderr, " [ label STRING ] [ scope SCOPE-ID ]\n");
@@ -768,6 +774,99 @@ static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
return 0;
}
+static __u32 ipadd_dump_magic = 0x47361222;
+
+static int ipadd_save_prep(void)
+{
+ int ret;
+
+ if (isatty(STDOUT_FILENO)) {
+ fprintf(stderr, "Not sending binary stream to stdout\n");
+ return -1;
+ }
+
+ ret = write(STDOUT_FILENO, &ipadd_dump_magic, sizeof(ipadd_dump_magic));
+ if (ret != sizeof(ipadd_dump_magic)) {
+ fprintf(stderr, "Can't write magic to dump file\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipadd_dump_check_magic(void)
+{
+ int ret;
+ __u32 magic = 0;
+
+ if (isatty(STDIN_FILENO)) {
+ fprintf(stderr, "Can't restore addr dump from a terminal\n");
+ return -1;
+ }
+
+ ret = fread(&magic, sizeof(magic), 1, stdin);
+ if (magic != ipadd_dump_magic) {
+ fprintf(stderr, "Magic mismatch (%d elems, %x magic)\n", ret, magic);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int save_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+ void *arg)
+{
+ int ret;
+
+ ret = write(STDOUT_FILENO, n, n->nlmsg_len);
+ if ((ret > 0) && (ret != n->nlmsg_len)) {
+ fprintf(stderr, "Short write while saving nlmsg\n");
+ ret = -EIO;
+ }
+
+ return ret == n->nlmsg_len ? 0 : ret;
+}
+
+static int show_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+{
+ struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+ printf("if%d:\n", ifa->ifa_index);
+ print_addrinfo(NULL, n, stdout);
+ return 0;
+}
+
+static int ipaddr_showdump(void)
+{
+ if (ipadd_dump_check_magic())
+ exit(-1);
+
+ exit(rtnl_from_file(stdin, &show_handler, NULL));
+}
+
+static int restore_handler(const struct sockaddr_nl *nl, struct nlmsghdr *n, void *arg)
+{
+ int ret;
+
+ n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
+
+ ll_init_map(&rth);
+
+ ret = rtnl_talk(&rth, n, 0, 0, n);
+ if ((ret < 0) && (errno == EEXIST))
+ ret = 0;
+
+ return ret;
+}
+
+static int ipaddr_restore(void)
+{
+ if (ipadd_dump_check_magic())
+ exit(-1);
+
+ exit(rtnl_from_file(stdin, &restore_handler, NULL));
+}
+
static void free_nlmsg_chain(struct nlmsg_chain *info)
{
struct nlmsg_list *l, *n;
@@ -902,7 +1001,7 @@ static int ipaddr_flush(void)
return 1;
}
-static int ipaddr_list_or_flush(int argc, char **argv, int flush)
+static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
{
struct nlmsg_chain linfo = { NULL, NULL};
struct nlmsg_chain ainfo = { NULL, NULL};
@@ -918,7 +1017,7 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush)
filter.group = INIT_NETDEV_GROUP;
- if (flush) {
+ if (action == IPADD_FLUSH) {
if (argc <= 0) {
fprintf(stderr, "Flush requires arguments.\n");
@@ -1005,9 +1104,26 @@ static int ipaddr_list_or_flush(int argc, char **argv, int flush)
}
}
- if (flush)
+ if (action == IPADD_FLUSH)
return ipaddr_flush();
+ if (action == IPADD_SAVE) {
+ if (ipadd_save_prep())
+ exit(1);
+
+ if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETADDR) < 0) {
+ perror("Cannot send dump request");
+ exit(1);
+ }
+
+ if (rtnl_dump_filter(&rth, save_nlmsg, stdout) < 0) {
+ fprintf(stderr, "Save terminated\n");
+ exit(1);
+ }
+
+ exit(0);
+ }
+
if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
perror("Cannot send dump request");
exit(1);
@@ -1055,7 +1171,7 @@ int ipaddr_list_link(int argc, char **argv)
{
preferred_family = AF_PACKET;
do_link = 1;
- return ipaddr_list_or_flush(argc, argv, 0);
+ return ipaddr_list_flush_or_save(argc, argv, IPADD_LIST);
}
void ipaddr_reset_filter(int oneline)
@@ -1271,7 +1387,7 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
int do_ipaddr(int argc, char **argv)
{
if (argc < 1)
- return ipaddr_list_or_flush(0, NULL, 0);
+ return ipaddr_list_flush_or_save(0, NULL, IPADD_LIST);
if (matches(*argv, "add") == 0)
return ipaddr_modify(RTM_NEWADDR, NLM_F_CREATE|NLM_F_EXCL, argc-1, argv+1);
if (matches(*argv, "change") == 0 ||
@@ -1283,9 +1399,15 @@ int do_ipaddr(int argc, char **argv)
return ipaddr_modify(RTM_DELADDR, 0, argc-1, argv+1);
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
|| matches(*argv, "lst") == 0)
- return ipaddr_list_or_flush(argc-1, argv+1, 0);
+ return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_LIST);
if (matches(*argv, "flush") == 0)
- return ipaddr_list_or_flush(argc-1, argv+1, 1);
+ return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_FLUSH);
+ if (matches(*argv, "save") == 0)
+ return ipaddr_list_flush_or_save(argc-1, argv+1, IPADD_SAVE);
+ if (matches(*argv, "showdump") == 0)
+ return ipaddr_showdump();
+ if (matches(*argv, "restore") == 0)
+ return ipaddr_restore();
if (matches(*argv, "help") == 0)
usage();
fprintf(stderr, "Command \"%s\" is unknown, try \"ip addr help\".\n", *argv);
^ permalink raw reply related
* [PATCH] tcp: Add TCP_USER_TIMEOUT negative value check
From: Hangbin Liu @ 2012-07-27 8:52 UTC (permalink / raw)
To: netdev; +Cc: hkchu, Hangbin Liu
TCP_USER_TIMEOUT is a TCP level socket option that takes an unsigned int. But
patch "tcp: Add TCP_USER_TIMEOUT socket option"(dca43c75) didn't check the negative
values. If a user assign -1 to it, the socket will set successfully and wait
for 4294967295 miliseconds. This patch add a negative value check to avoid
this issue.
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
---
net/ipv4/tcp.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 581ecf0..e7e6eea 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2681,7 +2681,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
/* Cap the max timeout in ms TCP will retry/retrans
* before giving up and aborting (ETIMEDOUT) a connection.
*/
- icsk->icsk_user_timeout = msecs_to_jiffies(val);
+ if (val < 0)
+ err = -EINVAL;
+ else
+ icsk->icsk_user_timeout = msecs_to_jiffies(val);
break;
default:
err = -ENOPROTOOPT;
--
1.7.7.6
^ permalink raw reply related
* Re: r8169, 3.5.0, does't work at all
From: Francois Romieu @ 2012-07-27 9:13 UTC (permalink / raw)
To: Denys Fedoryshchenko; +Cc: netdev
In-Reply-To: <e9e38770b95bc3664fdc642fdfb65a13@visp.net.lb>
Denys Fedoryshchenko <denys@visp.net.lb> :
[...]
> and i can search for more machines with similar cards.
Thanks.
There seems to be a more recent bios for this motherboard. Is the second
8168b an add-on or is it a dual-port DH61HO ?
You are using x86 32 with PAE and slub where I use x86 64 with slab.
I am building a x86 32 bits test configuration for a 8168b right now.
Let's see what happened during the 3.4 .. 3.5 cycle in the r8169 driver:
- You may revert 7dbb491878a2c51d372a8890fa45a8ff80358af1 ("r8169: avoid NAPI
scheduling delay.")
I can see a napi rtl8169_poll failing to schedule rtl_slow_event_work
when it is already scheduled on a different cpu, said rtl_slow_event_work
running while rtl8169_poll is delayed and rtl8169_poll stomping on the
IntrMask after rtl_slow_event_work did.
It does not tell why the device does not recover after the netdev watchdog
but it could be worth trying.
- You may revert 851e60221926a53344b4227879858bef841b0477 ("r8169: Config1 is
read-only on 8168c and later.") as well.
It changes the PMEEnable write ordering for the 8168b (RTL_GIGA_MAC_VER_17)
and a few others chipset.
--
Ueimor
^ permalink raw reply
* Re: [PATCH 00/16] Remove the ipv4 routing cache
From: Eric Dumazet @ 2012-07-27 10:01 UTC (permalink / raw)
To: David Miller; +Cc: alexander.duyck, netdev
In-Reply-To: <20120726.230246.219188476590178857.davem@davemloft.net>
From: Eric Dumazet <edumazet@google.com>
On Thu, 2012-07-26 at 23:02 -0700, David Miller wrote:
> From: David Miller <davem@davemloft.net>
> Date: Thu, 26 Jul 2012 20:08:46 -0700 (PDT)
>
> > A lot of the overhead comes from write traffic that results from
> > filling in the "fib_result" structure onto the callers stack.
>
> Here's the longer analysis of how things are now.
>
> There are several components to a route lookup result, and struct
> fib_result tries to encapsulate all of this.
>
> Another aspect is that our route tables are broken up into different
> datas tructures which reference each other, in order to save space.
>
> So the actual objects in the FIB trie are fib_alias structures, and
> those point to fib_info. There is a many to one relationship between
> FIB trie nodes and fib_info objects.
>
> The idea is that many routes have the same set of nexthops, metrics,
> preferred source address, etc.
>
> So one thing we return in the fib_result is a pointer to the fib_info
> and an index into the nexthop array (nh_sel). That's why we have all
> of these funny accessor's FIB_RES_X(res) which essentially provide
> res.fi->fib_nh[res.nh_sel].X
>
> 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.
>
> It seems also that the res->scope value can be cribbed from the
> fib_info as well.
>
> res->type is embedded in the fib_alias we select hanging off of the
> FIB trie node. And the res->prefixlen is taken from the FIB trie
> node.
>
> res->tclassid is problematic, because it comes from the FIB rules
> tables rather than the FIB trie. We used to store a full FIB rules
> pointer in the fib_result, but I reduced it down to just the u32
> tclassid.
>
> This whole area, as well as the FIB trie lookup itself, is an area
> ripe for a large number of small micro-optimizations that in the end
> make it's overhead much more reasonable.
>
> Another thing I haven't mentioned is that another part of FIB trie's
> overhead is that it does backtracking. The shorter prefixes sit at
> the top of the trie, so when it traverses down it does so until it
> can't get a match, then it walks back up to the root until it does
> have a match.
We also have cache line misses in this code, and we shouldn't have them
at all.
Following patch helps a lot in my case.
Thanks
[PATCH] ipv4: fib: avoid false sharing
Now IP route cache is removed, we should make sure fib structures
cant share cache lines with possibly often dirtied objects.
On x86, kmalloc-96 cache can be source of such problems.
Problem spotted with perf ... -e cache-misses ... while doing
a forwarding benchmark.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/net/ip_fib.h | 6 ++++++
net/ipv4/fib_frontend.c | 5 +----
net/ipv4/fib_semantics.c | 15 ++++++++-------
net/ipv4/fib_trie.c | 26 +++++++++-----------------
4 files changed, 24 insertions(+), 28 deletions(-)
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index e69c3a4..5b6a9b3 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -295,6 +295,12 @@ extern void fib_select_multipath(struct fib_result *res);
/* Exported by fib_trie.c */
extern void fib_trie_init(void);
extern struct fib_table *fib_trie_table(u32 id);
+static inline void *fib_zalloc(size_t size)
+{
+ /* We want to avoid possible false sharing */
+ return kzalloc(max_t(size_t, 128, size), GFP_KERNEL);
+}
+
static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
{
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 8732cc7..a3d0285 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1089,10 +1089,7 @@ static int __net_init ip_fib_net_init(struct net *net)
int err;
size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ;
- /* Avoid false sharing : Use at least a full cache line */
- size = max_t(size_t, size, L1_CACHE_BYTES);
-
- net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
+ net->ipv4.fib_table_hash = fib_zalloc(size);
if (net->ipv4.fib_table_hash == NULL)
return -ENOMEM;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index da0cc2e..a9f5e87 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -651,17 +651,17 @@ static inline unsigned int fib_laddr_hashfn(__be32 val)
((__force u32)val >> 14)) & mask;
}
-static struct hlist_head *fib_info_hash_alloc(int bytes)
+static struct hlist_head *fib_info_hash_alloc(size_t bytes)
{
if (bytes <= PAGE_SIZE)
- return kzalloc(bytes, GFP_KERNEL);
+ return fib_zalloc(bytes);
else
return (struct hlist_head *)
__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(bytes));
}
-static void fib_info_hash_free(struct hlist_head *hash, int bytes)
+static void fib_info_hash_free(struct hlist_head *hash, size_t bytes)
{
if (!hash)
return;
@@ -678,7 +678,8 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash,
{
struct hlist_head *old_info_hash, *old_laddrhash;
unsigned int old_size = fib_info_hash_size;
- unsigned int i, bytes;
+ unsigned int i;
+ size_t bytes;
spin_lock_bh(&fib_info_lock);
old_info_hash = fib_info_hash;
@@ -766,10 +767,10 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
unsigned int new_size = fib_info_hash_size << 1;
struct hlist_head *new_info_hash;
struct hlist_head *new_laddrhash;
- unsigned int bytes;
+ size_t bytes;
if (!new_size)
- new_size = 1;
+ new_size = 16;
bytes = new_size * sizeof(struct hlist_head *);
new_info_hash = fib_info_hash_alloc(bytes);
new_laddrhash = fib_info_hash_alloc(bytes);
@@ -783,7 +784,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
goto failure;
}
- fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
+ fi = fib_zalloc(sizeof(*fi) + nhs*sizeof(struct fib_nh));
if (fi == NULL)
goto failure;
if (cfg->fc_mx) {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 18cbc15..664152c 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -379,7 +379,7 @@ static inline void free_leaf_info(struct leaf_info *leaf)
static struct tnode *tnode_alloc(size_t size)
{
if (size <= PAGE_SIZE)
- return kzalloc(size, GFP_KERNEL);
+ return fib_zalloc(size);
else
return vzalloc(size);
}
@@ -449,7 +449,7 @@ static struct leaf *leaf_new(void)
static struct leaf_info *leaf_info_new(int plen)
{
- struct leaf_info *li = kmalloc(sizeof(struct leaf_info), GFP_KERNEL);
+ struct leaf_info *li = fib_zalloc(sizeof(struct leaf_info));
if (li) {
li->plen = plen;
li->mask_plen = ntohl(inet_make_mask(plen));
@@ -1969,32 +1969,24 @@ void __init fib_trie_init(void)
{
fn_alias_kmem = kmem_cache_create("ip_fib_alias",
sizeof(struct fib_alias),
- 0, SLAB_PANIC, NULL);
+ 0, SLAB_PANIC | SLAB_HWCACHE_ALIGN, NULL);
trie_leaf_kmem = kmem_cache_create("ip_fib_trie",
max(sizeof(struct leaf),
sizeof(struct leaf_info)),
- 0, SLAB_PANIC, NULL);
+ 0, SLAB_PANIC | SLAB_HWCACHE_ALIGN, NULL);
}
struct fib_table *fib_trie_table(u32 id)
{
struct fib_table *tb;
- struct trie *t;
-
- tb = kmalloc(sizeof(struct fib_table) + sizeof(struct trie),
- GFP_KERNEL);
- if (tb == NULL)
- return NULL;
-
- tb->tb_id = id;
- tb->tb_default = -1;
- tb->tb_num_default = 0;
-
- t = (struct trie *) tb->tb_data;
- memset(t, 0, sizeof(*t));
+ tb = fib_zalloc(sizeof(struct fib_table) + sizeof(struct trie));
+ if (tb) {
+ tb->tb_id = id;
+ tb->tb_default = -1;
+ }
return tb;
}
^ permalink raw reply related
* [PATCH iproute2 v2 0/3] CAN Filter/Classifier
From: Marc Kleine-Budde @ 2012-07-27 10:02 UTC (permalink / raw)
To: netdev; +Cc: linux-can, lartc, pisa, sojkam1, lisovy
Hello,
I'm reposting Rostislav's iproute2 patches, as Stephen requested,
during the v3.6 merge window with the corresponding kernel patches
already applied.
Changes since v1:
- use can.h generated by 'make headers_install' (tnx Stephen)
- remove some trailing whitespace
Now Rostislav original introduction message:
---
This classifier classifies CAN frames (AF_CAN) according to their
identifiers. This functionality can not be easily achieved with
existing classifiers, such as u32. This classifier can be used
with any available qdisc and it is able to classify both SFF
or EFF frames.
The filtering rules for EFF frames are stored in an array, which
is traversed during classification. A bitmap is used to store SFF
rules -- one bit for each ID.
More info about the project:
http://rtime.felk.cvut.cz/can/socketcan-qdisc-final.pdf
---
The following changes since commit fa1f7441a94670ecf5cbf8eb2ee19173437b5127:
Remove reference to multipath algorithms in usage (2012-07-26 16:12:20 -0700)
are available in the git repository at:
git://gitorious.org/linux-can/iproute2.git for-stephen
for you to fetch changes up to 1a83c82569763968200d2cabdf8f5bcbe7940f9b:
CAN Filter/Classifier -- Documentation (2012-07-27 11:26:06 +0200)
----------------------------------------------------------------
Rostislav Lisovy (3):
Add missing can.h
CAN Filter/Classifier -- Source code
CAN Filter/Classifier -- Documentation
include/linux/can.h | 161 ++++++++++++++++++++++++++++++++
include/linux/pkt_cls.h | 10 ++
man/man8/tc-can.8 | 97 +++++++++++++++++++
tc/Makefile | 1 +
tc/f_can.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 506 insertions(+)
create mode 100644 include/linux/can.h
create mode 100644 man/man8/tc-can.8
create mode 100644 tc/f_can.c
^ permalink raw reply
* [PATCH iproute2 v2 1/3] Add missing can.h
From: Marc Kleine-Budde @ 2012-07-27 10:02 UTC (permalink / raw)
To: netdev; +Cc: linux-can, lartc, pisa, sojkam1, lisovy, Marc Kleine-Budde
In-Reply-To: <1343383363-6174-1-git-send-email-mkl@pengutronix.de>
From: Rostislav Lisovy <lisovy@gmail.com>
This patch imports the can.h generated from linux kernel (v3.6 merge
window - v3.5-6970-g25918f9) with 'make headers_install'. It contains
defines necessary for working with AF_CAN packets.
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
[mkl: use 'make headers_install' in current kernel]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
Changes since v1:
- use can.h generated by 'make headers_install' (tnx Stephen)
include/linux/can.h | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 161 insertions(+)
create mode 100644 include/linux/can.h
diff --git a/include/linux/can.h b/include/linux/can.h
new file mode 100644
index 0000000..018055e
--- /dev/null
+++ b/include/linux/can.h
@@ -0,0 +1,161 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN network layer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Urs Thuermann <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error message frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28 : CAN identifier (11/29 bit)
+ * bit 29 : error message frame flag (0 = data frame, 1 = error message)
+ * bit 30 : remote transmission request flag (1 = rtr frame)
+ * bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+#define CAN_SFF_ID_BITS 11
+#define CAN_EFF_ID_BITS 29
+
+/*
+ * Controller Area Network Error Message Frame Mask structure
+ *
+ * bit 0-28 : error class mask (see include/linux/can/error.h)
+ * bit 29-31 : set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/* CAN payload length and DLC definitions according to ISO 11898-1 */
+#define CAN_MAX_DLC 8
+#define CAN_MAX_DLEN 8
+
+/* CAN FD payload length and DLC definitions according to ISO 11898-7 */
+#define CANFD_MAX_DLC 15
+#define CANFD_MAX_DLEN 64
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
+ * @can_dlc: frame payload length in byte (0 .. 8) aka data length code
+ * N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
+ * mapping of the 'data length code' to the real payload length
+ * @data: CAN frame payload (up to 8 byte)
+ */
+struct can_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
+ __u8 data[CAN_MAX_DLEN] __attribute__((aligned(8)));
+};
+
+/*
+ * defined bits for canfd_frame.flags
+ *
+ * As the default for CAN FD should be to support the high data rate in the
+ * payload section of the frame (HDR) and to support up to 64 byte in the
+ * data section (EDL) the bits are only set in the non-default case.
+ * Btw. as long as there's no real implementation for CAN FD network driver
+ * these bits are only preliminary.
+ *
+ * RX: NOHDR/NOEDL - info about received CAN FD frame
+ * ESI - bit from originating CAN controller
+ * TX: NOHDR/NOEDL - control per-frame settings if supported by CAN controller
+ * ESI - bit is set by local CAN controller
+ */
+#define CANFD_NOHDR 0x01 /* frame without high data rate */
+#define CANFD_NOEDL 0x02 /* frame without extended data length */
+#define CANFD_ESI 0x04 /* error state indicator */
+
+/**
+ * struct canfd_frame - CAN flexible data rate frame structure
+ * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
+ * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN)
+ * @flags: additional flags for CAN FD
+ * @__res0: reserved / padding
+ * @__res1: reserved / padding
+ * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte)
+ */
+struct canfd_frame {
+ canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ __u8 len; /* frame payload length in byte */
+ __u8 flags; /* additional flags for CAN FD */
+ __u8 __res0; /* reserved / padding */
+ __u8 __res1; /* reserved / padding */
+ __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));
+};
+
+#define CAN_MTU (sizeof(struct can_frame))
+#define CANFD_MTU (sizeof(struct canfd_frame))
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW 1 /* RAW sockets */
+#define CAN_BCM 2 /* Broadcast Manager */
+#define CAN_TP16 3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20 4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET 5 /* Bosch MCNet */
+#define CAN_ISOTP 6 /* ISO 15765-2 Transport Protocol */
+#define CAN_NPROTO 7
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family: address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr: protocol specific address information
+ */
+struct sockaddr_can {
+ __kernel_sa_family_t can_family;
+ int can_ifindex;
+ union {
+ /* transport protocol class address information (e.g. ISOTP) */
+ struct { canid_t rx_id, tx_id; } tp;
+
+ /* reserved for future CAN protocols address information */
+ } can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id: relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ * <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error message frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+ canid_t can_id;
+ canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
--
1.7.10
^ permalink raw reply related
* [PATCH iproute2 v2 2/3] CAN Filter/Classifier -- Source code
From: Marc Kleine-Budde @ 2012-07-27 10:02 UTC (permalink / raw)
To: netdev; +Cc: linux-can, lartc, pisa, sojkam1, lisovy, Marc Kleine-Budde
In-Reply-To: <1343383363-6174-1-git-send-email-mkl@pengutronix.de>
From: Rostislav Lisovy <lisovy@gmail.com>
This classifier classifies CAN frames (AF_CAN) according to their
identifiers. This functionality can not be easily achieved with
existing classifiers, such as u32. This classifier can be used
with any available qdisc and it is able to classify both SFF
or EFF frames.
The filtering rules for EFF frames are stored in an array, which
is traversed during classification. A bitmap is used to store SFF
rules -- one bit for each ID.
More info about the project:
http://rtime.felk.cvut.cz/can/socketcan-qdisc-final.pdf
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
Changes since v1:
- remove black line at end of file
include/linux/pkt_cls.h | 10 ++
tc/Makefile | 1 +
tc/f_can.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 248 insertions(+)
create mode 100644 tc/f_can.c
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index defbde2..83f9241 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -375,6 +375,16 @@ enum {
#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1)
+/* CAN filter */
+
+enum {
+ TCA_CANFLTR_UNSPEC,
+ TCA_CANFLTR_CLASSID,
+ TCA_CANFLTR_RULES,
+ __TCA_CANFLTR_MAX
+};
+
+#define TCA_CANFLTR_MAX (__TCA_CANFLTR_MAX - 1)
/* Cgroup classifier */
diff --git a/tc/Makefile b/tc/Makefile
index 64d93ad..1281568 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -22,6 +22,7 @@ TCMODULES += f_u32.o
TCMODULES += f_route.o
TCMODULES += f_fw.o
TCMODULES += f_basic.o
+TCMODULES += f_can.o
TCMODULES += f_flow.o
TCMODULES += f_cgroup.o
TCMODULES += q_dsmark.o
diff --git a/tc/f_can.c b/tc/f_can.c
new file mode 100644
index 0000000..531cf91
--- /dev/null
+++ b/tc/f_can.c
@@ -0,0 +1,237 @@
+/*
+ * f_can.c Filter for CAN packets
+ *
+ * This program is free software; you can distribute 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.
+ *
+ * Idea: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright: (c) 2011 Czech Technical University in Prague
+ * (c) 2011 Volkswagen Group Research
+ * Authors: Michal Sojka <sojkam1@fel.cvut.cz>
+ * Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ * Rostislav Lisovy <lisovy@gmail.cz>
+ * Funded by: Volkswagen Group Research
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <linux/if.h>
+#include <limits.h>
+#include <inttypes.h>
+#include "utils.h"
+#include "tc_util.h"
+#include "linux/can.h"
+
+#define RULES_SIZE 128 /* Maximum number of rules sent via the
+ netlink message during creation/configuration */
+
+
+static void canfltr_explain(void)
+{
+ fprintf(stderr, "Usage: ... can [ MATCHSPEC ] [ flowid FLOWID ]\n"
+ "\n"
+ "Where: MATCHSPEC := { sffid FILTERID | effid FILTERID |\n"
+ " MATCHSPEC ... }\n"
+ " FILTERID := CANID[:MASK]\n"
+ "\n"
+ "NOTE: CLASSID, CANID, MASK is parsed as hexadecimal input.\n");
+}
+
+static int canfltr_parse_opt(struct filter_util *qu, char *handle,
+ int argc, char **argv, struct nlmsghdr *n)
+{
+ struct tcmsg *t = NLMSG_DATA(n);
+ struct rtattr *tail;
+ struct can_filter canfltr_rules[RULES_SIZE];
+ int rules_count = 0;
+ long h = 0;
+ canid_t can_id;
+ canid_t can_mask;
+
+ if (!argc)
+ return 0;
+
+ if (handle) {
+ h = strtol(handle, NULL, 0);
+ if (h == LONG_MIN || h == LONG_MAX) {
+ fprintf(stderr, "Illegal handle \"%s\", must be numeric.\n",
+ handle);
+ return -1;
+ }
+ }
+
+ t->tcm_handle = h;
+
+ tail = NLMSG_TAIL(n);
+ addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
+
+ while (argc > 0) {
+ if (matches(*argv, "sffid") == 0) {
+ /* parse SFF CAN ID optionally with mask */
+ if (rules_count >= RULES_SIZE) {
+ fprintf(stderr, "Too much rules on input. "
+ "Maximum number of rules is: %d\n",
+ RULES_SIZE);
+ return -1;
+ }
+
+ NEXT_ARG();
+
+ if (sscanf(*argv, "%"SCNx32 ":" "%"SCNx32,
+ &can_id, &can_mask) != 2) {
+ if (sscanf(*argv, "%"SCNx32, &can_id) != 1) {
+ fprintf(stderr, "Improperly formed CAN "
+ "ID & mask '%s'\n", *argv);
+ return -1;
+ } else
+ can_mask = CAN_SFF_MASK;
+ }
+
+ /* we do not support extra handling for RTR frames
+ due to the bitmap approach */
+ if (can_id & ~CAN_SFF_MASK) {
+ fprintf(stderr, "ID 0x%lx exceeded standard CAN ID range.\n",
+ (unsigned long)can_id);
+ return -1;
+ }
+
+ canfltr_rules[rules_count].can_id = can_id;
+ canfltr_rules[rules_count].can_mask =
+ (can_mask & CAN_SFF_MASK);
+ rules_count++;
+
+ } else if (matches(*argv, "effid") == 0) {
+ /* parse EFF CAN ID optionally with mask */
+ if (rules_count >= RULES_SIZE) {
+ fprintf(stderr, "Too much rules on input. "
+ "Maximum number of rules is: %d\n",
+ RULES_SIZE);
+ return -1;
+ }
+
+ NEXT_ARG();
+
+ if (sscanf(*argv, "%"SCNx32 ":" "%"SCNx32, &can_id, &can_mask) != 2) {
+ if (sscanf(*argv, "%"SCNx32, &can_id) != 1) {
+ fprintf(stderr, "Improperly formed CAN ID & mask '%s'\n", *argv);
+ return -1;
+ } else
+ can_mask = CAN_EFF_MASK;
+ }
+
+ if (can_id & ~CAN_EFF_MASK) {
+ fprintf(stderr, "ID 0x%lx exceeded extended CAN ID range.",
+ (unsigned long)can_id);
+ return -1;
+ }
+
+ canfltr_rules[rules_count].can_id =
+ can_id | CAN_EFF_FLAG;
+ canfltr_rules[rules_count].can_mask =
+ (can_mask & CAN_EFF_MASK) | CAN_EFF_FLAG;
+ rules_count++;
+
+ } else if (matches(*argv, "classid") == 0 || strcmp(*argv, "flowid") == 0) {
+ unsigned handle;
+ NEXT_ARG();
+ if (get_tc_classid(&handle, *argv)) {
+ fprintf(stderr, "Illegal \"classid\"\n");
+ return -1;
+ }
+ addattr_l(n, MAX_MSG, TCA_CANFLTR_CLASSID, &handle, 4);
+
+ } else if (strcmp(*argv, "help") == 0) {
+ canfltr_explain();
+ return -1;
+
+ } else {
+ fprintf(stderr, "What is \"%s\"?\n", *argv);
+ canfltr_explain();
+ return -1;
+ }
+ argc--; argv++;
+ }
+
+ addattr_l(n, MAX_MSG, TCA_CANFLTR_RULES, &canfltr_rules,
+ sizeof(struct can_filter) * rules_count);
+
+ tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
+ return 0;
+}
+
+/* When "tc filter show dev XY" is executed, function canfltr_walk() (in
+ * kernel) is called (which calls canfltr_dump() for each instance of a
+ * filter) which sends information about each instance of a filter to
+ * userspace -- to this function which parses the message and prints it.
+ */
+static int canfltr_print_opt(struct filter_util *qu, FILE *f,
+ struct rtattr *opt, __u32 handle)
+{
+ struct rtattr *tb[TCA_CANFLTR_MAX+1];
+ struct can_filter *canfltr_rules = NULL;
+ int rules_count = 0;
+ int i;
+
+ if (opt == NULL)
+ return 0;
+
+ parse_rtattr_nested(tb, TCA_CANFLTR_MAX, opt);
+
+ if (handle)
+ fprintf(f, "handle 0x%x ", handle);
+
+
+ if (tb[TCA_BASIC_CLASSID]) {
+ SPRINT_BUF(b1); /* allocates buffer b1 */
+ fprintf(f, "flowid %s ",
+ sprint_tc_classid(*(__u32 *)RTA_DATA(tb[TCA_BASIC_CLASSID]), b1));
+ }
+
+ if (tb[TCA_CANFLTR_RULES]) {
+ if (RTA_PAYLOAD(tb[TCA_CANFLTR_RULES]) < sizeof(struct can_filter))
+ return -1;
+
+ canfltr_rules = RTA_DATA(tb[TCA_CANFLTR_RULES]);
+ rules_count = (RTA_PAYLOAD(tb[TCA_CANFLTR_RULES]) /
+ sizeof(struct can_filter));
+
+ for (i = 0; i < rules_count; i++) {
+ struct can_filter *pcfltr = &canfltr_rules[i];
+
+ if (pcfltr->can_id & CAN_EFF_FLAG) {
+ if (pcfltr->can_mask == (CAN_EFF_FLAG|CAN_EFF_MASK))
+ fprintf(f, "effid 0x%"PRIX32" ",
+ pcfltr->can_id & CAN_EFF_MASK);
+ else
+ fprintf(f, "effid 0x%"PRIX32":0x%"PRIX32" ",
+ pcfltr->can_id & CAN_EFF_MASK,
+ pcfltr->can_mask & CAN_EFF_MASK);
+ } else {
+ if (pcfltr->can_mask == CAN_SFF_MASK)
+ fprintf(f, "sffid 0x%"PRIX32" ",
+ pcfltr->can_id);
+ else
+ fprintf(f, "sffid 0x%"PRIX32":0x%"PRIX32" ",
+ pcfltr->can_id,
+ pcfltr->can_mask);
+ }
+ }
+ }
+
+ return 0;
+}
+
+struct filter_util can_filter_util = {
+ .id = "can",
+ .parse_fopt = canfltr_parse_opt,
+ .print_fopt = canfltr_print_opt,
+};
--
1.7.10
^ permalink raw reply related
* [PATCH iproute2 v2 3/3] CAN Filter/Classifier -- Documentation
From: Marc Kleine-Budde @ 2012-07-27 10:02 UTC (permalink / raw)
To: netdev; +Cc: linux-can, lartc, pisa, sojkam1, lisovy, Marc Kleine-Budde
In-Reply-To: <1343383363-6174-1-git-send-email-mkl@pengutronix.de>
From: Rostislav Lisovy <lisovy@gmail.com>
Manpage describing usage of CAN Filter.
Signed-off-by: Rostislav Lisovy <lisovy@gmail.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
Changes since v1:
- remove trailing whitespace
man/man8/tc-can.8 | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
create mode 100644 man/man8/tc-can.8
diff --git a/man/man8/tc-can.8 b/man/man8/tc-can.8
new file mode 100644
index 0000000..d60e872
--- /dev/null
+++ b/man/man8/tc-can.8
@@ -0,0 +1,97 @@
+.TH CAN 8 "8 May 2012" "iproute2" "Linux"
+.SH NAME
+CAN \- Controller Area Network classifier
+.SH SYNOPSIS
+.B tc filter ... dev
+DEV
+.B parent
+CLASSID
+.B [ prio
+PRIORITY
+.B ] [ protocol can ] [ handle
+HANDLE
+.B ] can [
+MATCHSPEC
+.B ] [ flowid
+FLOWID
+.B ]
+
+.B CLASSID := major:minor
+.br
+.B FLOWID := major:minor
+.br
+.B MATCHSPEC := { sffid
+FILTERID
+.B | effid
+FILTERID
+.B | MATCHSPEC ... }
+.br
+.B FILTERID := canid[:mask]
+
+.BR CLASSID ,
+.BR FLOWID ,
+.BR canid
+and
+.B mask
+are parsed as hexadecimal input.
+
+
+.SH DESCRIPTION
+The CAN classifier may be used with any available
+.B qdisc
+on Controller Area Network (CAN) frames passed through AF_CAN
+networking subsystem. The classifier classifies CAN frames according
+to their identifiers. It can be used on CAN frames with both SFF or
+EFF identifiers.
+
+It is possible to add CAN classifier to any qdisc configured on any networking
+device, however it will ignore non-CAN packets.
+
+
+.SH CLASSIFICATION
+The filtering rules for EFF frames are stored in an array, which is traversed
+during classification. This means that the worst-case time needed for
+classification of EFF frames increases with the number of configured rules.
+
+The filter implements an optimization for matching SFF frames using a bitmap
+with one bit for every ID. With this optimization, the classification time
+for SFF frames is nearly constant independently of the number of rules.
+
+.SH EXAMPLE
+This example shows how to set
+.B prio qdisc
+with
+.B CAN
+classifier.
+
+.nf
+tc qdisc add dev can0 root handle 1: prio
+
+tc filter add dev can0 parent 1:0 prio 1 handle 0xa \\
+ can sffid 0x7ff:0xf flowid 1:1
+tc filter add dev can0 parent 1:0 prio 2 handle 0xb \\
+ can sffid 0xC0:0x7ff effid 0x80:0x7ff flowid 1:2
+tc filter add dev can0 parent 1:0 prio 3 \\
+ can sffid 0x80:0x7ff flowid 1:2
+tc filter add dev can0 parent 1:0 prio 4 \\
+ can sffid 0x0:0x0 effid 0x0:0x0 flowid 1:3
+.fi
+
+
+.SH BUGS
+The maximum number or rules passed from
+.BR tc(8)
+utility to CAN classifier is fixed. The limit is set at compilation time
+(default is 128).
+
+
+.SH SEE ALSO
+.BR tc(8)
+
+
+.SH AUTHORS
+Michal Sojka <sojkam1@fel.cvut.cz>, Pavel Pisa <pisa@cmp.felk.cvut.cz>,
+Rostislav Lisovy <lisovy@gmail.cz>.
+
+This manpage maintained by Rostislav Lisovy <lisovy@gmail.com>
+
--
1.7.10
^ permalink raw reply related
* RE: [PATCH 1/2] iproute: Add magic cookie to route dump file
From: David Laight @ 2012-07-27 10:46 UTC (permalink / raw)
To: Pavel Emelyanov, Stephen Hemminger, Linux Netdev List
In-Reply-To: <50121F4D.8090606@parallels.com>
> In order to somehow verify that a blob contains route dump a
> 4-bytes magic is put at the head of the data and is checked
> on restore.
Wouldn't a hash/checksum be useful as well?
Especially if it uneditable data.
David
^ permalink raw reply
* Re: [PATCH v2 0/7] TCP Fast Open client
From: Michael Kerrisk @ 2012-07-27 11:42 UTC (permalink / raw)
To: Yuchung Cheng; +Cc: davem, hkchu, edumazet, ncardwell, sivasankar, netdev
In-Reply-To: <1342645307-17772-1-git-send-email-ycheng@google.com>
Yuchung,
On Wed, Jul 18, 2012 at 11:01 PM, Yuchung Cheng <ycheng@google.com> wrote:
> ChangeLog since v1:
> - Reduce tons of code by storing Fast Open stats in the TCP metrics :)
> - Clarify the purpose of using an experimental option in patch 1/7
>
> This patch series implement the client functionality of TCP Fast Open.
> TCP Fast Open (TFO) allows data to be carried in the SYN and SYN-ACK
> packets and consumed by the receiving end during the initial connection
> handshake, thus providing a saving of up to one full round trip time (RTT)
> compared to standard TCP requiring a three-way handshake (3WHS) to
> complete before data can be exchanged.
>
> The protocol change is detailed in the IETF internet draft at
> http://www.ietf.org/id/draft-ietf-tcpm-fastopen-00.txt . The research
This URL appears to be invalid. I assume the following is the correct
current version:
http://tools.ietf.org/html/draft-ietf-tcpm-fastopen-01
?
Is there some sample client and server userspace test code available?
Thanks,
Michael
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
^ permalink raw reply
* [patch] USB: kaweth.c: use GFP_ATOMIC under spin_lock
From: Dan Carpenter @ 2012-07-27 11:46 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, netdev, kernel-janitors
The problem is that we call this with a spin lock held. The call tree
is:
kaweth_start_xmit() holds kaweth->device_lock.
-> kaweth_async_set_rx_mode()
-> kaweth_control()
-> kaweth_internal_control_msg()
The kaweth_internal_control_msg() function is only called from
kaweth_control() which used GFP_ATOMIC for its allocations.
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
---
Applies to Linus's tree.
diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c
index d8ad552..c3d0349 100644
--- a/drivers/net/usb/kaweth.c
+++ b/drivers/net/usb/kaweth.c
@@ -1314,7 +1314,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev,
int retv;
int length = 0; /* shut up GCC */
- urb = usb_alloc_urb(0, GFP_NOIO);
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
^ permalink raw reply related
* [PATCH] net: fix rtnetlink IFF_PROMISC and IFF_ALLMULTI handling
From: Jiri Benc @ 2012-07-27 12:58 UTC (permalink / raw)
To: netdev; +Cc: Max Matveev
When device flags are set using rtnetlink, IFF_PROMISC and IFF_ALLMULTI
flags are handled specially. Function dev_change_flags sets IFF_PROMISC and
IFF_ALLMULTI bits in dev->gflags according to the passed value but
do_setlink passes a result of rtnl_dev_combine_flags which takes those bits
from dev->flags.
This can be easily trigerred by doing:
tcpdump -i eth0 &
ip l s up eth0
ip sets IFF_UP flag in ifi_flags and ifi_change, which is combined with
IFF_PROMISC by rtnl_dev_combine_flags, causing __dev_change_flags to set
IFF_PROMISC in gflags.
Reported-by: Max Matveev <makc@redhat.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
---
net/core/rtnetlink.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -659,6 +659,12 @@ static void set_operstate(struct net_dev
}
}
+static unsigned int rtnl_dev_get_flags(const struct net_device *dev)
+{
+ return (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI)) |
+ (dev->gflags & (IFF_PROMISC | IFF_ALLMULTI));
+}
+
static unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
const struct ifinfomsg *ifm)
{
@@ -667,7 +673,7 @@ static unsigned int rtnl_dev_combine_fla
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
if (ifm->ifi_change)
flags = (flags & ifm->ifi_change) |
- (dev->flags & ~ifm->ifi_change);
+ (rtnl_dev_get_flags(dev) & ~ifm->ifi_change);
return flags;
}
^ permalink raw reply
* [PATCH net-next] GRE over IPv6
From: Kozlov Dmitry @ 2012-07-27 12:25 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 122 bytes --]
GRE over IPv6 imlementation.
Based on ipv4/ip_gre.c and ipv6/ip6_tunnel.c
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
---
[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 61495 bytes --]
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_gre.c | 1228 ++++++++++++++++++++++----------------------
net/ipv6/ip6_tunnel.c | 86 +++-
9 files changed, 767 insertions(+), 627 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..c0e9be1 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -31,6 +31,7 @@
#define NEXTHDR_HOP 0 /* Hop-by-hop option header. */
#define NEXTHDR_TCP 6 /* TCP segment. */
#define NEXTHDR_UDP 17 /* UDP message. */
+#define NEXTHDR_GRE 47 /* GRE header. */
#define NEXTHDR_IPV6 41 /* IPv6 in IPv6 */
#define NEXTHDR_ROUTING 43 /* Routing header. */
#define NEXTHDR_FRAGMENT 44 /* Fragmentation/reassembly header. */
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_gre.c b/net/ipv6/ip6_gre.c
new file mode 100644
index 0000000..5c89df2
--- /dev/null
+++ b/net/ipv6/ip6_gre.c
@@ -0,0 +1,1816 @@
+/*
+ * GRE over IPv6 protocol decoder.
+ *
+ * Authors: Dmitry Kozlov (xeb@mail.ru)
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/hash.h>
+#include <linux/if_tunnel.h>
+#include <linux/ip6_tunnel.h>
+
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <net/protocol.h>
+#include <net/addrconf.h>
+#include <net/arp.h>
+#include <net/checksum.h>
+#include <net/dsfield.h>
+#include <net/inet_ecn.h>
+#include <net/xfrm.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/rtnetlink.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ip6_tunnel.h>
+
+
+#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
+#define IPV6_TCLASS_SHIFT 20
+
+#define HASH_SIZE_SHIFT 5
+#define HASH_SIZE (1 << HASH_SIZE_SHIFT)
+
+static int ip6gre_net_id __read_mostly;
+struct ip6gre_net {
+ struct ip6_tnl __rcu *tunnels[4][HASH_SIZE];
+
+ struct net_device *fb_tunnel_dev;
+};
+
+static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
+static int ip6gre_tunnel_init(struct net_device *dev);
+static void ip6gre_tunnel_setup(struct net_device *dev);
+static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
+static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu);
+
+/* Tunnel hash table */
+
+/*
+ 4 hash tables:
+
+ 3: (remote,local)
+ 2: (remote,*)
+ 1: (*,local)
+ 0: (*,*)
+
+ We require exact key match i.e. if a key is present in packet
+ it will match only tunnel with the same key; if it is not present,
+ it will match only keyless tunnel.
+
+ All keysless packets, if not matched configured keyless tunnels
+ will match fallback tunnel.
+ */
+
+#define HASH_KEY(key) (((__force u32)key^((__force u32)key>>4))&(HASH_SIZE - 1))
+static u32 HASH_ADDR(const struct in6_addr *addr)
+{
+ //u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
+ u32 hash = ipv6_addr_hash(addr);
+
+ return hash_32(hash, HASH_SIZE_SHIFT);
+}
+
+#define tunnels_r_l tunnels[3]
+#define tunnels_r tunnels[2]
+#define tunnels_l tunnels[1]
+#define tunnels_wc tunnels[0]
+/*
+ * Locking : hash tables are protected by RCU and RTNL
+ */
+
+#define for_each_ip_tunnel_rcu(start) \
+ for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
+
+/* often modified stats are per cpu, other are shared (netdev->stats) */
+struct pcpu_tstats {
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
+};
+
+static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *tot)
+{
+ int i;
+
+ for_each_possible_cpu(i) {
+ const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
+ u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+ unsigned int start;
+
+ do {
+ start = u64_stats_fetch_begin_bh(&tstats->syncp);
+ rx_packets = tstats->rx_packets;
+ tx_packets = tstats->tx_packets;
+ rx_bytes = tstats->rx_bytes;
+ tx_bytes = tstats->tx_bytes;
+ } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+
+ tot->rx_packets += rx_packets;
+ tot->tx_packets += tx_packets;
+ tot->rx_bytes += rx_bytes;
+ tot->tx_bytes += tx_bytes;
+ }
+
+ tot->multicast = dev->stats.multicast;
+ tot->rx_crc_errors = dev->stats.rx_crc_errors;
+ tot->rx_fifo_errors = dev->stats.rx_fifo_errors;
+ tot->rx_length_errors = dev->stats.rx_length_errors;
+ tot->rx_errors = dev->stats.rx_errors;
+ tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
+ tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
+ tot->tx_dropped = dev->stats.tx_dropped;
+ tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
+ tot->tx_errors = dev->stats.tx_errors;
+
+ return tot;
+}
+
+/* Given src, dst and key, find appropriate for input tunnel. */
+
+static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
+ const struct in6_addr *remote, const struct in6_addr *local,
+ __be32 key, __be16 gre_proto)
+{
+ struct net *net = dev_net(dev);
+ int link = dev->ifindex;
+ unsigned int h0 = HASH_ADDR(remote);
+ unsigned int h1 = HASH_KEY(key);
+ struct ip6_tnl *t, *cand = NULL;
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+ int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
+ ARPHRD_ETHER : ARPHRD_IP6GRE;
+ int score, cand_score = 4;
+
+ for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
+ if (!ipv6_addr_equal(local, &t->parms.laddr) ||
+ !ipv6_addr_equal(remote, &t->parms.raddr) ||
+ key != t->parms.i_key ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
+ if (t->dev->type != ARPHRD_IP6GRE &&
+ t->dev->type != dev_type)
+ continue;
+
+ score = 0;
+ if (t->parms.link != link)
+ score |= 1;
+ if (t->dev->type != dev_type)
+ score |= 2;
+ if (score == 0)
+ return t;
+
+ if (score < cand_score) {
+ cand = t;
+ cand_score = score;
+ }
+ }
+
+ for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
+ if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
+ key != t->parms.i_key ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
+ if (t->dev->type != ARPHRD_IP6GRE &&
+ t->dev->type != dev_type)
+ continue;
+
+ score = 0;
+ if (t->parms.link != link)
+ score |= 1;
+ if (t->dev->type != dev_type)
+ score |= 2;
+ if (score == 0)
+ return t;
+
+ if (score < cand_score) {
+ cand = t;
+ cand_score = score;
+ }
+ }
+
+ for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
+ if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
+ (!ipv6_addr_equal(local, &t->parms.raddr) ||
+ !ipv6_addr_is_multicast(local))) ||
+ key != t->parms.i_key ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
+ if (t->dev->type != ARPHRD_IP6GRE &&
+ t->dev->type != dev_type)
+ continue;
+
+ score = 0;
+ if (t->parms.link != link)
+ score |= 1;
+ if (t->dev->type != dev_type)
+ score |= 2;
+ if (score == 0)
+ return t;
+
+ if (score < cand_score) {
+ cand = t;
+ cand_score = score;
+ }
+ }
+
+ for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
+ if (t->parms.i_key != key ||
+ !(t->dev->flags & IFF_UP))
+ continue;
+
+ if (t->dev->type != ARPHRD_IP6GRE &&
+ t->dev->type != dev_type)
+ continue;
+
+ score = 0;
+ if (t->parms.link != link)
+ score |= 1;
+ if (t->dev->type != dev_type)
+ score |= 2;
+ if (score == 0)
+ return t;
+
+ if (score < cand_score) {
+ cand = t;
+ cand_score = score;
+ }
+ }
+
+ if (cand != NULL)
+ return cand;
+
+ dev = ign->fb_tunnel_dev;
+ if (dev->flags & IFF_UP)
+ return netdev_priv(dev);
+
+ return NULL;
+}
+
+static struct ip6_tnl __rcu **__ip6gre_bucket(struct ip6gre_net *ign, const struct __ip6_tnl_parm *p)
+{
+ const struct in6_addr *remote = &p->raddr;
+ const struct in6_addr *local = &p->laddr;
+ unsigned int h = HASH_KEY(p->i_key);
+ int prio = 0;
+
+ if (!ipv6_addr_any(local))
+ prio |= 1;
+ if (!ipv6_addr_any(remote) && !ipv6_addr_is_multicast(remote)) {
+ prio |= 2;
+ h ^= HASH_ADDR(remote);
+ }
+
+ return &ign->tunnels[prio][h];
+}
+
+static inline struct ip6_tnl __rcu **ip6gre_bucket(struct ip6gre_net *ign,
+ const struct ip6_tnl *t)
+{
+ return __ip6gre_bucket(ign, &t->parms);
+}
+
+static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+ struct ip6_tnl __rcu **tp = ip6gre_bucket(ign, t);
+
+ rcu_assign_pointer(t->next, rtnl_dereference(*tp));
+ rcu_assign_pointer(*tp, t);
+}
+
+static void ip6gre_tunnel_unlink(struct ip6gre_net *ign, struct ip6_tnl *t)
+{
+ struct ip6_tnl __rcu **tp;
+ struct ip6_tnl *iter;
+
+ for (tp = ip6gre_bucket(ign, t);
+ (iter = rtnl_dereference(*tp)) != NULL;
+ tp = &iter->next) {
+ if (t == iter) {
+ rcu_assign_pointer(*tp, t->next);
+ break;
+ }
+ }
+}
+
+static struct ip6_tnl *ip6gre_tunnel_find(struct net *net,
+ const struct __ip6_tnl_parm *parms,
+ int type)
+{
+ const struct in6_addr *remote = &parms->raddr;
+ const struct in6_addr *local = &parms->laddr;
+ __be32 key = parms->i_key;
+ int link = parms->link;
+ struct ip6_tnl *t;
+ struct ip6_tnl __rcu **tp;
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+ for (tp = __ip6gre_bucket(ign, parms);
+ (t = rtnl_dereference(*tp)) != NULL;
+ tp = &t->next)
+ if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ ipv6_addr_equal(remote, &t->parms.raddr) &&
+ key == t->parms.i_key &&
+ link == t->parms.link &&
+ type == t->dev->type)
+ break;
+
+ return t;
+}
+
+static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net,
+ const struct __ip6_tnl_parm *parms, int create)
+{
+ struct ip6_tnl *t, *nt;
+ struct net_device *dev;
+ char name[IFNAMSIZ];
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+ t = ip6gre_tunnel_find(net, parms, ARPHRD_IP6GRE);
+ if (t || !create)
+ return t;
+
+ if (parms->name[0])
+ strlcpy(name, parms->name, IFNAMSIZ);
+ else
+ strcpy(name, "ip6gre%d");
+
+ dev = alloc_netdev(sizeof(*t), name, ip6gre_tunnel_setup);
+ if (!dev)
+ return NULL;
+
+ dev_net_set(dev, net);
+
+ nt = netdev_priv(dev);
+ nt->parms = *parms;
+ dev->rtnl_link_ops = &ip6gre_link_ops;
+
+ nt->dev = dev;
+ ip6gre_tnl_link_config(nt, 1);
+
+ if (register_netdevice(dev) < 0)
+ goto failed_free;
+
+ /* Can use a lockless transmit, unless we generate output sequences */
+ if (!(nt->parms.o_flags & GRE_SEQ))
+ dev->features |= NETIF_F_LLTX;
+
+ dev_hold(dev);
+ ip6gre_tunnel_link(ign, nt);
+ return nt;
+
+failed_free:
+ free_netdev(dev);
+ return NULL;
+}
+
+static void ip6gre_tunnel_uninit(struct net_device *dev)
+{
+ struct net *net = dev_net(dev);
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+ ip6gre_tunnel_unlink(ign, netdev_priv(dev));
+ dev_put(dev);
+}
+
+
+static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
+ __be16 *p = (__be16 *)(ipv6h + 1);
+ int grehlen = sizeof(ipv6h) + 4;
+ struct ip6_tnl *t;
+ __be16 flags;
+
+ flags = p[0];
+ if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
+ if (flags&(GRE_VERSION|GRE_ROUTING))
+ return;
+ if (flags&GRE_KEY) {
+ grehlen += 4;
+ if (flags&GRE_CSUM)
+ grehlen += 4;
+ }
+ }
+
+ /* If only 8 bytes returned, keyed message will be dropped here */
+ if (skb_headlen(skb) < grehlen)
+ return;
+
+ rcu_read_lock();
+
+ t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
+ flags & GRE_KEY ?
+ *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
+ p[1]);
+ if (t == NULL)
+ goto out;
+
+ switch (type) {
+ __u32 teli;
+ struct ipv6_tlv_tnl_enc_lim *tel;
+ __u32 mtu;
+ case ICMPV6_DEST_UNREACH:
+ net_warn_ratelimited("%s: Path to destination invalid or inactive!\n",
+ t->parms.name);
+ break;
+ case ICMPV6_TIME_EXCEED:
+ if (code == ICMPV6_EXC_HOPLIMIT) {
+ net_warn_ratelimited("%s: Too small hop limit or routing loop in tunnel!\n",
+ t->parms.name);
+ }
+ break;
+ case ICMPV6_PARAMPROB:
+ teli = 0;
+ if (code == ICMPV6_HDR_FIELD)
+ 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];
+ if (tel->encap_limit == 0) {
+ net_warn_ratelimited("%s: Too small encapsulation limit or routing loop in tunnel!\n",
+ t->parms.name);
+ }
+ } else {
+ net_warn_ratelimited("%s: Recipient unable to parse tunneled packet!\n",
+ t->parms.name);
+ }
+ break;
+ case ICMPV6_PKT_TOOBIG:
+ mtu = info - offset;
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ t->dev->mtu = mtu;
+ break;
+ }
+
+ if (time_before(jiffies, t->err_time + IP6TUNNEL_ERR_TIMEO))
+ t->err_count++;
+ else
+ t->err_count = 1;
+ t->err_time = jiffies;
+out:
+ rcu_read_unlock();
+}
+
+static inline void ip6gre_ecn_decapsulate_ipv4(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb)
+{
+ __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
+
+ if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+ ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
+
+ if (INET_ECN_is_ce(dsfield))
+ IP_ECN_set_ce(ip_hdr(skb));
+}
+
+static inline void ip6gre_ecn_decapsulate_ipv6(const struct ip6_tnl *t, const struct ipv6hdr *ipv6h, struct sk_buff *skb)
+{
+ if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
+ ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
+
+ if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
+ IP6_ECN_set_ce(ipv6_hdr(skb));
+}
+
+static int ip6gre_rcv(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ipv6h;
+ u8 *h;
+ __be16 flags;
+ __sum16 csum = 0;
+ __be32 key = 0;
+ u32 seqno = 0;
+ struct ip6_tnl *tunnel;
+ int offset = 4;
+ __be16 gre_proto;
+
+ if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
+ goto drop_nolock;
+
+ ipv6h = ipv6_hdr(skb);
+ h = skb->data;
+ flags = *(__be16 *)h;
+
+ if (flags&(GRE_CSUM|GRE_KEY|GRE_ROUTING|GRE_SEQ|GRE_VERSION)) {
+ /* - Version must be 0.
+ - We do not support routing headers.
+ */
+ if (flags&(GRE_VERSION|GRE_ROUTING))
+ goto drop_nolock;
+
+ if (flags&GRE_CSUM) {
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ csum = csum_fold(skb->csum);
+ if (!csum)
+ break;
+ /* fall through */
+ case CHECKSUM_NONE:
+ skb->csum = 0;
+ csum = __skb_checksum_complete(skb);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+ offset += 4;
+ }
+ if (flags&GRE_KEY) {
+ key = *(__be32 *)(h + offset);
+ offset += 4;
+ }
+ if (flags&GRE_SEQ) {
+ seqno = ntohl(*(__be32 *)(h + offset));
+ offset += 4;
+ }
+ }
+
+ gre_proto = *(__be16 *)(h + 2);
+
+ rcu_read_lock();
+ if ((tunnel = ip6gre_tunnel_lookup(skb->dev,
+ &ipv6h->saddr, &ipv6h->daddr, key,
+ gre_proto))) {
+ struct pcpu_tstats *tstats;
+
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
+ goto drop;
+
+ if (!ip6_tnl_rcv_ctl(tunnel, &ipv6h->daddr, &ipv6h->saddr)) {
+ tunnel->dev->stats.rx_dropped++;
+ goto drop;
+ }
+
+ secpath_reset(skb);
+
+ skb->protocol = gre_proto;
+ /* WCCP version 1 and 2 protocol decoding.
+ * - Change protocol to IP
+ * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
+ */
+ if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
+ skb->protocol = htons(ETH_P_IP);
+ if ((*(h + offset) & 0xF0) != 0x40)
+ offset += 4;
+ }
+
+ skb->mac_header = skb->network_header;
+ __pskb_pull(skb, offset);
+ skb_postpull_rcsum(skb, skb_transport_header(skb), offset);
+ skb->pkt_type = PACKET_HOST;
+
+ if (((flags&GRE_CSUM) && csum) ||
+ (!(flags&GRE_CSUM) && tunnel->parms.i_flags&GRE_CSUM)) {
+ tunnel->dev->stats.rx_crc_errors++;
+ tunnel->dev->stats.rx_errors++;
+ goto drop;
+ }
+ if (tunnel->parms.i_flags&GRE_SEQ) {
+ if (!(flags&GRE_SEQ) ||
+ (tunnel->i_seqno && (s32)(seqno - tunnel->i_seqno) < 0)) {
+ tunnel->dev->stats.rx_fifo_errors++;
+ tunnel->dev->stats.rx_errors++;
+ goto drop;
+ }
+ tunnel->i_seqno = seqno + 1;
+ }
+
+ /* Warning: All skb pointers will be invalidated! */
+ if (tunnel->dev->type == ARPHRD_ETHER) {
+ if (!pskb_may_pull(skb, ETH_HLEN)) {
+ tunnel->dev->stats.rx_length_errors++;
+ tunnel->dev->stats.rx_errors++;
+ goto drop;
+ }
+
+ ipv6h = ipv6_hdr(skb);
+ skb->protocol = eth_type_trans(skb, tunnel->dev);
+ skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+ }
+
+ tstats = this_cpu_ptr(tunnel->dev->tstats);
+ u64_stats_update_begin(&tstats->syncp);
+ tstats->rx_packets++;
+ tstats->rx_bytes += skb->len;
+ u64_stats_update_end(&tstats->syncp);
+
+ __skb_tunnel_rx(skb, tunnel->dev);
+
+ skb_reset_network_header(skb);
+ if (skb->protocol == htons(ETH_P_IP))
+ ip6gre_ecn_decapsulate_ipv4(tunnel, ipv6h, skb);
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ ip6gre_ecn_decapsulate_ipv6(tunnel, ipv6h, skb);
+
+ netif_rx(skb);
+
+ rcu_read_unlock();
+ return 0;
+ }
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0);
+
+drop:
+ rcu_read_unlock();
+drop_nolock:
+ kfree_skb(skb);
+ return 0;
+}
+
+struct ipv6_tel_txoption {
+ struct ipv6_txoptions ops;
+ __u8 dst_opt[8];
+};
+
+static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit)
+{
+ memset(opt, 0, sizeof(struct ipv6_tel_txoption));
+
+ opt->dst_opt[2] = IPV6_TLV_TNL_ENCAP_LIMIT;
+ opt->dst_opt[3] = 1;
+ opt->dst_opt[4] = encap_limit;
+ opt->dst_opt[5] = IPV6_TLV_PADN;
+ opt->dst_opt[6] = 1;
+
+ opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt;
+ opt->ops.opt_nflen = 8;
+}
+
+static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
+ struct net_device *dev,
+ __u8 dsfield,
+ struct flowi6 *fl6,
+ int encap_limit,
+ __u32 *pmtu)
+{
+ struct net *net = dev_net(dev);
+ struct ip6_tnl *tunnel = netdev_priv(dev);
+ struct net_device *tdev; /* Device to other host */
+ struct ipv6hdr *ipv6h; /* Our new IP header */
+ unsigned int max_headroom; /* The extra header space needed */
+ int gre_hlen;
+ struct in6_addr *daddr, daddr_b;
+ struct ipv6_tel_txoption opt;
+ int mtu;
+ struct dst_entry *dst = NULL, *ndst = NULL;
+ struct net_device_stats *stats = &tunnel->dev->stats;
+ int err = -1;
+ u8 proto;
+ int pkt_len;
+
+ if (dev->type == ARPHRD_ETHER)
+ IPCB(skb)->flags = 0;
+
+ if (dev->header_ops && dev->type == ARPHRD_IP6GRE) {
+ gre_hlen = 0;
+ ipv6h = (struct ipv6hdr *)skb->data;
+ daddr = &ipv6h->daddr;
+ } else {
+ gre_hlen = tunnel->hlen;
+ daddr = &tunnel->parms.raddr;
+ }
+
+ if (ipv6_addr_any(daddr)) {
+ /* NBMA tunnel */
+
+ if (skb_dst(skb) == NULL) {
+ dev->stats.tx_fifo_errors++;
+ goto tx_error;
+ }
+
+ if (skb->protocol == htons(ETH_P_IPV6)) {
+ const struct in6_addr *addr6;
+ struct neighbour *neigh;
+ int addr_type;
+
+ neigh = dst_neigh_lookup(skb_dst(skb), &ipv6_hdr(skb)->daddr);
+ if (neigh == NULL)
+ goto tx_error;
+
+ addr6 = (const struct in6_addr *)&neigh->primary_key;
+ addr_type = ipv6_addr_type(addr6);
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ addr6 = &ipv6_hdr(skb)->daddr;
+ addr_type = ipv6_addr_type(addr6);
+ }
+
+ daddr_b = *addr6;
+ daddr = &daddr_b;
+
+ neigh_release(neigh);
+ } else
+ goto tx_error;
+ }
+
+ fl6->daddr = *daddr;
+
+ if (!fl6->flowi6_mark)
+ dst = ip6_tnl_dst_check(tunnel);
+
+ if (!dst) {
+ ndst = ip6_route_output(net, NULL, fl6);
+
+ if (ndst->error)
+ goto tx_err_link_failure;
+ ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(fl6), NULL, 0);
+ if (IS_ERR(ndst)) {
+ err = PTR_ERR(ndst);
+ ndst = NULL;
+ goto tx_err_link_failure;
+ }
+ dst = ndst;
+ }
+
+ tdev = dst->dev;
+
+ if (tdev == dev) {
+ stats->collisions++;
+ net_warn_ratelimited("%s: Local routing loop detected!\n",
+ tunnel->parms.name);
+ goto tx_err_dst_release;
+ }
+
+ mtu = dst_mtu(dst) - sizeof (*ipv6h);
+ if (encap_limit >= 0) {
+ max_headroom += 8;
+ mtu -= 8;
+ }
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+ if (skb_dst(skb))
+ skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
+ if (skb->len > mtu) {
+ *pmtu = mtu;
+ err = -EMSGSIZE;
+ goto tx_err_dst_release;
+ }
+
+ if (tunnel->err_count > 0) {
+ if (time_before(jiffies,
+ tunnel->err_time + IP6TUNNEL_ERR_TIMEO)) {
+ tunnel->err_count--;
+
+ dst_link_failure(skb);
+ } else
+ tunnel->err_count = 0;
+ }
+
+ max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len;
+
+ if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
+ (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+ if (max_headroom > dev->needed_headroom)
+ dev->needed_headroom = max_headroom;
+ if (!new_skb)
+ goto tx_err_dst_release;
+
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
+ consume_skb(skb);
+ skb = new_skb;
+ }
+
+ skb_dst_drop(skb);
+
+ if (fl6->flowi6_mark) {
+ skb_dst_set(skb, dst);
+ ndst = NULL;
+ } else {
+ skb_dst_set_noref(skb, dst);
+ }
+
+ skb->transport_header = skb->network_header;
+
+ proto = NEXTHDR_GRE;
+ if (encap_limit >= 0) {
+ init_tel_txopt(&opt, encap_limit);
+ ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
+ }
+
+ skb_push(skb, gre_hlen);
+ skb_reset_network_header(skb);
+
+ /*
+ * Push down and install the IP header.
+ */
+ ipv6h = ipv6_hdr(skb);
+ *(__be32*)ipv6h = fl6->flowlabel | htonl(0x60000000);
+ dsfield = INET_ECN_encapsulate(0, dsfield);
+ ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
+ ipv6h->hop_limit = tunnel->parms.hop_limit;
+ ipv6h->nexthdr = proto;
+ ipv6h->saddr = fl6->saddr;
+ ipv6h->daddr = fl6->daddr;
+
+ ((__be16 *)(ipv6h + 1))[0] = tunnel->parms.o_flags;
+ ((__be16 *)(ipv6h + 1))[1] = (dev->type == ARPHRD_ETHER) ?
+ htons(ETH_P_TEB) : skb->protocol;
+
+ if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) {
+ __be32 *ptr = (__be32 *)(((u8 *)ipv6h) + tunnel->hlen - 4);
+
+ if (tunnel->parms.o_flags&GRE_SEQ) {
+ ++tunnel->o_seqno;
+ *ptr = htonl(tunnel->o_seqno);
+ ptr--;
+ }
+ if (tunnel->parms.o_flags&GRE_KEY) {
+ *ptr = tunnel->parms.o_key;
+ ptr--;
+ }
+ if (tunnel->parms.o_flags&GRE_CSUM) {
+ *ptr = 0;
+ *(__sum16 *)ptr = ip_compute_csum((void *)(ipv6h+1), skb->len - sizeof(struct ipv6hdr));
+ }
+ }
+
+ nf_reset(skb);
+ pkt_len = skb->len;
+ err = ip6_local_out(skb);
+
+ if (net_xmit_eval(err) == 0) {
+ struct pcpu_tstats *tstats = this_cpu_ptr(tunnel->dev->tstats);
+
+ tstats->tx_bytes += pkt_len;
+ tstats->tx_packets++;
+ } else {
+ stats->tx_errors++;
+ stats->tx_aborted_errors++;
+ }
+
+ if (ndst)
+ ip6_tnl_dst_store(tunnel, ndst);
+
+ return 0;
+tx_err_link_failure:
+ stats->tx_carrier_errors++;
+ dst_link_failure(skb);
+tx_err_dst_release:
+ dst_release(ndst);
+tx_error:
+ return err;
+}
+
+static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ const struct iphdr *iph = ip_hdr(skb);
+ int encap_limit = -1;
+ struct flowi6 fl6;
+ __u8 dsfield;
+ __u32 mtu;
+ int err;
+
+ if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+ encap_limit = t->parms.encap_limit;
+
+ memcpy(&fl6, &t->fl.u.ip6, sizeof (fl6));
+ fl6.flowi6_proto = IPPROTO_IPIP;
+
+ dsfield = ipv4_get_dsfield(iph);
+
+ if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
+ fl6.flowlabel |= htonl((__u32)iph->tos << IPV6_TCLASS_SHIFT)
+ & IPV6_TCLASS_MASK;
+ if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
+ fl6.flowi6_mark = skb->mark;
+
+ err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
+ if (err != 0) {
+ /* XXX: send ICMP error even if DF is not set. */
+ if (err == -EMSGSIZE)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+ htonl(mtu));
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ int encap_limit = -1;
+ __u16 offset;
+ struct flowi6 fl6;
+ __u8 dsfield;
+ __u32 mtu;
+ int err;
+
+ if (ipv6_addr_equal(&t->parms.raddr, &ipv6h->saddr))
+ return -1;
+
+ 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];
+ if (tel->encap_limit == 0) {
+ icmpv6_send(skb, ICMPV6_PARAMPROB,
+ ICMPV6_HDR_FIELD, offset + 2);
+ return -1;
+ }
+ encap_limit = tel->encap_limit - 1;
+ } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+ encap_limit = t->parms.encap_limit;
+
+ memcpy(&fl6, &t->fl.u.ip6, sizeof (fl6));
+ fl6.flowi6_proto = IPPROTO_IPV6;
+
+ dsfield = ipv6_get_dsfield(ipv6h);
+ if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
+ fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
+ if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
+ fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
+ if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
+ fl6.flowi6_mark = skb->mark;
+
+ err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
+ if (err != 0) {
+ if (err == -EMSGSIZE)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own
+ * @t: the outgoing tunnel device
+ * @hdr: IPv6 header from the incoming packet
+ *
+ * Description:
+ * Avoid trivial tunneling loop by checking that tunnel exit-point
+ * doesn't match source of incoming packet.
+ *
+ * Return:
+ * 1 if conflict,
+ * 0 else
+ **/
+
+static inline bool ip6gre_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
+{
+ return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
+}
+
+static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ int encap_limit = -1;
+ struct flowi6 fl6;
+ __u32 mtu;
+ int err;
+
+ if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+ encap_limit = t->parms.encap_limit;
+
+ memcpy(&fl6, &t->fl.u.ip6, sizeof (fl6));
+ fl6.flowi6_proto = skb->protocol;
+
+ err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu);
+
+ return err;
+}
+
+static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net_device_stats *stats = &t->dev->stats;
+ int ret;
+
+ if (!ip6_tnl_xmit_ctl(t))
+ return -1;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ ret = ip6gre_xmit_ipv4(skb, dev);
+ break;
+ case htons(ETH_P_IPV6):
+ ret = ip6gre_xmit_ipv6(skb, dev);
+ break;
+ default:
+ ret = ip6gre_xmit_other(skb, dev);
+ break;
+ }
+
+ if (ret < 0)
+ goto tx_err;
+
+ return NETDEV_TX_OK;
+
+tx_err:
+ stats->tx_errors++;
+ stats->tx_dropped++;
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
+{
+ struct net_device *dev = t->dev;
+ struct __ip6_tnl_parm *p = &t->parms;
+ struct flowi6 *fl6 = &t->fl.u.ip6;
+ int addend = sizeof(struct ipv6hdr) + 4;
+
+ if (dev->type != ARPHRD_ETHER) {
+ memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
+ memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));
+ }
+
+ /* Set up flowi template */
+ fl6->saddr = p->laddr;
+ fl6->daddr = p->raddr;
+ fl6->flowi6_oif = p->link;
+ fl6->flowlabel = 0;
+
+ if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
+ fl6->flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
+ if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
+ fl6->flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;
+
+ p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV|IP6_TNL_F_CAP_PER_PACKET);
+ p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr);
+
+ if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV && dev->type != ARPHRD_ETHER)
+ dev->flags |= IFF_POINTOPOINT;
+ else
+ dev->flags &= ~IFF_POINTOPOINT;
+
+ dev->iflink = p->link;
+
+ /* Precalculate GRE options length */
+ if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
+ if (t->parms.o_flags&GRE_CSUM)
+ addend += 4;
+ if (t->parms.o_flags&GRE_KEY)
+ addend += 4;
+ if (t->parms.o_flags&GRE_SEQ)
+ addend += 4;
+ }
+
+ if (p->flags & IP6_TNL_F_CAP_XMIT) {
+ int strict = (ipv6_addr_type(&p->raddr) &
+ (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
+
+ struct rt6_info *rt = rt6_lookup(dev_net(dev),
+ &p->raddr, &p->laddr,
+ p->link, strict);
+
+ if (rt == NULL)
+ return;
+
+ if (rt->dst.dev) {
+ dev->hard_header_len = rt->dst.dev->hard_header_len + addend;
+
+ if (set_mtu) {
+ dev->mtu = rt->dst.dev->mtu - addend;
+ if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+ dev->mtu-=8;
+
+ if (dev->mtu < IPV6_MIN_MTU)
+ dev->mtu = IPV6_MIN_MTU;
+ }
+ }
+ dst_release(&rt->dst);
+ }
+
+ t->hlen = addend;
+}
+
+static int ip6gre_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p, int set_mtu)
+{
+ t->parms.laddr = p->laddr;
+ t->parms.raddr = p->raddr;
+ t->parms.flags = p->flags;
+ t->parms.hop_limit = p->hop_limit;
+ t->parms.encap_limit = p->encap_limit;
+ t->parms.flowinfo = p->flowinfo;
+ t->parms.link = p->link;
+ t->parms.proto = p->proto;
+ t->parms.i_key = p->i_key;
+ t->parms.o_key = p->o_key;
+ t->parms.i_flags = p->i_flags;
+ t->parms.o_flags = p->o_flags;
+ ip6_tnl_dst_reset(t);
+ ip6gre_tnl_link_config(t, set_mtu);
+ return 0;
+}
+
+static void ip6gre_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *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->i_key = u->i_key;
+ p->o_key = u->o_key;
+ p->i_flags = u->i_flags;
+ p->o_flags = u->o_flags;
+ memcpy(p->name, u->name, sizeof(u->name));
+}
+
+static void ip6gre_tnl_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p)
+{
+ u->proto = IPPROTO_GRE;
+ 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->i_key = p->i_key;
+ u->o_key = p->o_key;
+ u->i_flags = p->i_flags;
+ u->o_flags = p->o_flags;
+ memcpy(u->name, p->name, sizeof(u->name));
+}
+
+static int ip6gre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int err = 0;
+ struct ip6_tnl_parm2 p;
+ struct __ip6_tnl_parm p1;
+ struct ip6_tnl *t;
+ struct net *net = dev_net(dev);
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+
+ switch (cmd) {
+ case SIOCGETTUNNEL:
+ t = NULL;
+ if (dev == ign->fb_tunnel_dev) {
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
+ err = -EFAULT;
+ break;
+ }
+ ip6gre_tnl_parm_from_user(&p1, &p);
+ t = ip6gre_tunnel_locate(net, &p1, 0);
+ }
+ if (t == NULL)
+ t = netdev_priv(dev);
+ ip6gre_tnl_parm_to_user(&p, &t->parms);
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+ err = -EFAULT;
+ break;
+
+ case SIOCADDTUNNEL:
+ case SIOCCHGTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+
+ err = -EINVAL;
+ if ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))
+ goto done;
+
+ if (!(p.i_flags&GRE_KEY))
+ p.i_key = 0;
+ if (!(p.o_flags&GRE_KEY))
+ p.o_key = 0;
+
+ ip6gre_tnl_parm_from_user(&p1, &p);
+ t = ip6gre_tunnel_locate(net, &p1, cmd == SIOCADDTUNNEL);
+
+ if (dev != ign->fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
+ if (t != NULL) {
+ if (t->dev != dev) {
+ err = -EEXIST;
+ break;
+ }
+ } else {
+ t = netdev_priv(dev);
+
+ ip6gre_tunnel_unlink(ign, t);
+ synchronize_net();
+ ip6gre_tnl_change(t, &p1, 1);
+ ip6gre_tunnel_link(ign, t);
+ netdev_state_change(dev);
+ }
+ }
+
+ if (t) {
+ err = 0;
+
+ ip6gre_tnl_parm_to_user(&p, &t->parms);
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
+ err = -EFAULT;
+ } else
+ err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ break;
+
+ case SIOCDELTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto done;
+
+ if (dev == ign->fb_tunnel_dev) {
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
+ goto done;
+ err = -ENOENT;
+ ip6gre_tnl_parm_from_user(&p1, &p);
+ if ((t = ip6gre_tunnel_locate(net, &p1, 0)) == NULL)
+ goto done;
+ err = -EPERM;
+ if (t == netdev_priv(ign->fb_tunnel_dev))
+ goto done;
+ dev = t->dev;
+ }
+ unregister_netdevice(dev);
+ err = 0;
+ break;
+
+ default:
+ err = -EINVAL;
+ }
+
+done:
+ return err;
+}
+
+static int ip6gre_tunnel_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct ip6_tnl *tunnel = netdev_priv(dev);
+ if (new_mtu < 68 ||
+ new_mtu > 0xFFF8 - dev->hard_header_len - tunnel->hlen)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type,
+ const void *daddr, const void *saddr, unsigned int len)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen);
+ __be16 *p = (__be16 *)(ipv6h+1);
+
+ *(__be32*)ipv6h = t->fl.u.ip6.flowlabel | htonl(0x60000000);
+ ipv6h->hop_limit = t->parms.hop_limit;
+ ipv6h->nexthdr = NEXTHDR_GRE;
+ ipv6h->saddr = t->parms.laddr;
+ ipv6h->daddr = t->parms.raddr;
+
+ p[0] = t->parms.o_flags;
+ p[1] = htons(type);
+
+ /*
+ * Set the source hardware address.
+ */
+
+ if (saddr)
+ memcpy(&ipv6h->saddr, saddr, sizeof(struct in6_addr));
+ if (daddr)
+ memcpy(&ipv6h->daddr, daddr, sizeof(struct in6_addr));
+ if (!ipv6_addr_any(&ipv6h->daddr))
+ return t->hlen;
+
+ return -t->hlen;
+}
+
+static int ip6gre_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) skb_mac_header(skb);
+ memcpy(haddr, &ipv6h->saddr, sizeof(struct in6_addr));
+ return sizeof(struct in6_addr);
+}
+
+static const struct header_ops ip6gre_header_ops = {
+ .create = ip6gre_header,
+ .parse = ip6gre_header_parse,
+};
+
+static const struct net_device_ops ip6gre_netdev_ops = {
+ .ndo_init = ip6gre_tunnel_init,
+ .ndo_uninit = ip6gre_tunnel_uninit,
+ .ndo_start_xmit = ip6gre_tunnel_xmit,
+ .ndo_do_ioctl = ip6gre_tunnel_ioctl,
+ .ndo_change_mtu = ip6gre_tunnel_change_mtu,
+ .ndo_get_stats64 = ip6gre_get_stats64,
+};
+
+static void ip6gre_dev_free(struct net_device *dev)
+{
+ free_percpu(dev->tstats);
+ free_netdev(dev);
+}
+
+static void ip6gre_tunnel_setup(struct net_device *dev)
+{
+ struct ip6_tnl *t;
+
+ dev->netdev_ops = &ip6gre_netdev_ops;
+ dev->destructor = ip6gre_dev_free;
+
+ dev->type = ARPHRD_IP6GRE;
+ dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr) + 4;
+ dev->mtu = ETH_DATA_LEN - sizeof(struct ipv6hdr) - 4;
+ t = netdev_priv(dev);
+ if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
+ dev->mtu -= 8;
+ dev->flags |= IFF_NOARP;
+ dev->iflink = 0;
+ dev->addr_len = sizeof(struct in6_addr);
+ dev->features |= NETIF_F_NETNS_LOCAL;
+ dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+}
+
+static int ip6gre_tunnel_init(struct net_device *dev)
+{
+ struct ip6_tnl *tunnel;
+
+ tunnel = netdev_priv(dev);
+
+ tunnel->dev = dev;
+ strcpy(tunnel->parms.name, dev->name);
+
+ memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr));
+ memcpy(dev->broadcast, &tunnel->parms.raddr, sizeof(struct in6_addr));
+
+ if (ipv6_addr_any(&tunnel->parms.raddr))
+ dev->header_ops = &ip6gre_header_ops;
+
+ dev->tstats = alloc_percpu(struct pcpu_tstats);
+ if (!dev->tstats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ip6gre_fb_tunnel_init(struct net_device *dev)
+{
+ struct ip6_tnl *tunnel = netdev_priv(dev);
+
+ tunnel->dev = dev;
+ strcpy(tunnel->parms.name, dev->name);
+
+ tunnel->hlen = sizeof(struct ipv6hdr) + 4;
+
+ dev_hold(dev);
+}
+
+
+static struct inet6_protocol ip6gre_protocol __read_mostly = {
+ .handler = ip6gre_rcv,
+ .err_handler = ip6gre_err,
+ .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
+};
+
+static void ip6gre_destroy_tunnels(struct ip6gre_net *ign, struct list_head *head)
+{
+ int prio;
+
+ for (prio = 0; prio < 4; prio++) {
+ int h;
+ for (h = 0; h < HASH_SIZE; h++) {
+ struct ip6_tnl *t;
+
+ t = rtnl_dereference(ign->tunnels[prio][h]);
+
+ while (t != NULL) {
+ unregister_netdevice_queue(t->dev, head);
+ t = rtnl_dereference(t->next);
+ }
+ }
+ }
+}
+
+static int __net_init ip6gre_init_net(struct net *net)
+{
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+ int err;
+
+ ign->fb_tunnel_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6gre0",
+ ip6gre_tunnel_setup);
+ if (!ign->fb_tunnel_dev) {
+ err = -ENOMEM;
+ goto err_alloc_dev;
+ }
+ dev_net_set(ign->fb_tunnel_dev, net);
+
+ ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
+ ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
+
+ if ((err = register_netdev(ign->fb_tunnel_dev)))
+ goto err_reg_dev;
+
+ rcu_assign_pointer(ign->tunnels_wc[0],
+ netdev_priv(ign->fb_tunnel_dev));
+ return 0;
+
+err_reg_dev:
+ ip6gre_dev_free(ign->fb_tunnel_dev);
+err_alloc_dev:
+ return err;
+}
+
+static void __net_exit ip6gre_exit_net(struct net *net)
+{
+ struct ip6gre_net *ign;
+ LIST_HEAD(list);
+
+ ign = net_generic(net, ip6gre_net_id);
+ rtnl_lock();
+ ip6gre_destroy_tunnels(ign, &list);
+ unregister_netdevice_many(&list);
+ rtnl_unlock();
+}
+
+static struct pernet_operations ip6gre_net_ops = {
+ .init = ip6gre_init_net,
+ .exit = ip6gre_exit_net,
+ .id = &ip6gre_net_id,
+ .size = sizeof(struct ip6gre_net),
+};
+
+static int ip6gre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ __be16 flags;
+
+ if (!data)
+ return 0;
+
+ flags = 0;
+ if (data[IFLA_GRE_IFLAGS])
+ flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]);
+ if (data[IFLA_GRE_OFLAGS])
+ flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
+ if (flags & (GRE_VERSION|GRE_ROUTING))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ip6gre_tap_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ struct in6_addr daddr;
+
+ if (tb[IFLA_ADDRESS]) {
+ if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
+ return -EINVAL;
+ if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
+ return -EADDRNOTAVAIL;
+ }
+
+ if (!data)
+ goto out;
+
+ if (data[IFLA_GRE_REMOTE]) {
+ nla_memcpy(&daddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr));
+ if (ipv6_addr_any(&daddr))
+ return -EINVAL;
+ }
+
+out:
+ return ip6gre_tunnel_validate(tb, data);
+}
+
+
+static void ip6gre_netlink_parms(struct nlattr *data[],
+ struct __ip6_tnl_parm *parms)
+{
+ memset(parms, 0, sizeof(*parms));
+
+ if (!data)
+ return;
+
+ if (data[IFLA_GRE_LINK])
+ parms->link = nla_get_u32(data[IFLA_GRE_LINK]);
+
+ if (data[IFLA_GRE_IFLAGS])
+ parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]);
+
+ if (data[IFLA_GRE_OFLAGS])
+ parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]);
+
+ if (data[IFLA_GRE_IKEY])
+ parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]);
+
+ if (data[IFLA_GRE_OKEY])
+ parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]);
+
+ if (data[IFLA_GRE_LOCAL])
+ nla_memcpy(&parms->laddr, data[IFLA_GRE_LOCAL], sizeof(struct in6_addr));
+
+ if (data[IFLA_GRE_REMOTE])
+ nla_memcpy(&parms->raddr, data[IFLA_GRE_REMOTE], sizeof(struct in6_addr));
+
+ if (data[IFLA_GRE_TTL])
+ parms->hop_limit = nla_get_u8(data[IFLA_GRE_TTL]);
+
+ //if (data[IFLA_GRE_TOS])
+ // parms->priority = nla_get_u8(data[IFLA_GRE_TOS]);
+
+ if (data[IFLA_GRE_ENCAP_LIMIT])
+ parms->encap_limit = nla_get_u8(data[IFLA_GRE_ENCAP_LIMIT]);
+
+ if (data[IFLA_GRE_FLOWINFO])
+ parms->flowinfo = nla_get_u32(data[IFLA_GRE_FLOWINFO]);
+
+ if (data[IFLA_GRE_FLAGS])
+ parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]);
+}
+
+static int ip6gre_tap_init(struct net_device *dev)
+{
+ struct ip6_tnl *tunnel;
+
+ tunnel = netdev_priv(dev);
+
+ tunnel->dev = dev;
+ strcpy(tunnel->parms.name, dev->name);
+
+ ip6gre_tnl_link_config(tunnel, 1);
+
+ dev->tstats = alloc_percpu(struct pcpu_tstats);
+ if (!dev->tstats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct net_device_ops ip6gre_tap_netdev_ops = {
+ .ndo_init = ip6gre_tap_init,
+ .ndo_uninit = ip6gre_tunnel_uninit,
+ .ndo_start_xmit = ip6gre_tunnel_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = ip6gre_tunnel_change_mtu,
+ .ndo_get_stats64 = ip6gre_get_stats64,
+};
+
+static void ip6gre_tap_setup(struct net_device *dev)
+{
+
+ ether_setup(dev);
+
+ dev->netdev_ops = &ip6gre_tap_netdev_ops;
+ dev->destructor = ip6gre_dev_free;
+
+ dev->iflink = 0;
+ dev->features |= NETIF_F_NETNS_LOCAL;
+}
+
+static int ip6gre_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct ip6_tnl *nt;
+ struct net *net = dev_net(dev);
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+ int err;
+
+ nt = netdev_priv(dev);
+ ip6gre_netlink_parms(data, &nt->parms);
+
+ if (ip6gre_tunnel_find(net, &nt->parms, dev->type))
+ return -EEXIST;
+
+ if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
+ eth_hw_addr_random(dev);
+
+ ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]);
+
+ /* Can use a lockless transmit, unless we generate output sequences */
+ if (!(nt->parms.o_flags & GRE_SEQ))
+ dev->features |= NETIF_F_LLTX;
+
+ err = register_netdevice(dev);
+ if (err)
+ goto out;
+
+ dev_hold(dev);
+ ip6gre_tunnel_link(ign, nt);
+
+out:
+ return err;
+}
+
+static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct ip6_tnl *t, *nt;
+ struct net *net = dev_net(dev);
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
+ struct __ip6_tnl_parm p;
+
+ if (dev == ign->fb_tunnel_dev)
+ return -EINVAL;
+
+ nt = netdev_priv(dev);
+ ip6gre_netlink_parms(data, &p);
+
+ t = ip6gre_tunnel_locate(net, &p, 0);
+
+ if (t) {
+ if (t->dev != dev)
+ return -EEXIST;
+ } else {
+ t = nt;
+
+ ip6gre_tunnel_unlink(ign, t);
+ ip6gre_tnl_change(t, &p, !tb[IFLA_MTU]);
+ ip6gre_tunnel_link(ign, t);
+ netdev_state_change(dev);
+ }
+
+ return 0;
+}
+
+static size_t ip6gre_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_GRE_LINK */
+ nla_total_size(4) +
+ /* IFLA_GRE_IFLAGS */
+ nla_total_size(2) +
+ /* IFLA_GRE_OFLAGS */
+ nla_total_size(2) +
+ /* IFLA_GRE_IKEY */
+ nla_total_size(4) +
+ /* IFLA_GRE_OKEY */
+ nla_total_size(4) +
+ /* IFLA_GRE_LOCAL */
+ nla_total_size(4) +
+ /* IFLA_GRE_REMOTE */
+ nla_total_size(4) +
+ /* IFLA_GRE_TTL */
+ nla_total_size(1) +
+ /* IFLA_GRE_TOS */
+ nla_total_size(1) +
+ /* IFLA_GRE_ENCAP_LIMIT */
+ nla_total_size(1) +
+ /* IFLA_GRE_FLOWINFO */
+ nla_total_size(4) +
+ /* IFLA_GRE_FLAGS */
+ nla_total_size(4) +
+ 0;
+}
+
+static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct __ip6_tnl_parm *p = &t->parms;
+
+ if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
+ nla_put_be16(skb, IFLA_GRE_IFLAGS, p->i_flags) ||
+ nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) ||
+ nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
+ nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
+ nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->raddr) ||
+ nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->laddr) ||
+ nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
+ /*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/
+ nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
+ nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) ||
+ nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = {
+ [IFLA_GRE_LINK] = { .type = NLA_U32 },
+ [IFLA_GRE_IFLAGS] = { .type = NLA_U16 },
+ [IFLA_GRE_OFLAGS] = { .type = NLA_U16 },
+ [IFLA_GRE_IKEY] = { .type = NLA_U32 },
+ [IFLA_GRE_OKEY] = { .type = NLA_U32 },
+ [IFLA_GRE_LOCAL] = { .len = FIELD_SIZEOF(struct ipv6hdr, saddr) },
+ [IFLA_GRE_REMOTE] = { .len = FIELD_SIZEOF(struct ipv6hdr, daddr) },
+ [IFLA_GRE_TTL] = { .type = NLA_U8 },
+ //[IFLA_GRE_TOS] = { .type = NLA_U8 },
+ [IFLA_GRE_ENCAP_LIMIT] = { .type = NLA_U8 },
+ [IFLA_GRE_FLOWINFO] = { .type = NLA_U32 },
+ [IFLA_GRE_FLAGS] = { .type = NLA_U32 },
+// [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 },
+};
+
+static struct rtnl_link_ops ip6gre_link_ops __read_mostly = {
+ .kind = "ip6gre",
+ .maxtype = IFLA_GRE_MAX,
+ .policy = ip6gre_policy,
+ .priv_size = sizeof(struct ip6_tnl),
+ .setup = ip6gre_tunnel_setup,
+ .validate = ip6gre_tunnel_validate,
+ .newlink = ip6gre_newlink,
+ .changelink = ip6gre_changelink,
+ .get_size = ip6gre_get_size,
+ .fill_info = ip6gre_fill_info,
+};
+
+static struct rtnl_link_ops ip6gre_tap_ops __read_mostly = {
+ .kind = "ip6gretap",
+ .maxtype = IFLA_GRE_MAX,
+ .policy = ip6gre_policy,
+ .priv_size = sizeof(struct ip6_tnl),
+ .setup = ip6gre_tap_setup,
+ .validate = ip6gre_tap_validate,
+ .newlink = ip6gre_newlink,
+ .changelink = ip6gre_changelink,
+ .get_size = ip6gre_get_size,
+ .fill_info = ip6gre_fill_info,
+};
+
+/*
+ * And now the modules code and kernel interface.
+ */
+
+static int __init ip6gre_init(void)
+{
+ int err;
+
+ pr_info("GRE over IPv6 tunneling driver\n");
+
+ err = register_pernet_device(&ip6gre_net_ops);
+ if (err < 0)
+ return err;
+
+ err = inet6_add_protocol(&ip6gre_protocol, IPPROTO_GRE);
+ if (err < 0) {
+ pr_info("%s: can't add protocol\n", __func__);
+ goto add_proto_failed;
+ }
+
+ err = rtnl_link_register(&ip6gre_link_ops);
+ if (err < 0)
+ goto rtnl_link_failed;
+
+ err = rtnl_link_register(&ip6gre_tap_ops);
+ if (err < 0)
+ goto tap_ops_failed;
+
+out:
+ return err;
+
+tap_ops_failed:
+ rtnl_link_unregister(&ip6gre_link_ops);
+rtnl_link_failed:
+ inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
+add_proto_failed:
+ unregister_pernet_device(&ip6gre_net_ops);
+ goto out;
+}
+
+static void __exit ip6gre_fini(void)
+{
+ rtnl_link_unregister(&ip6gre_tap_ops);
+ rtnl_link_unregister(&ip6gre_link_ops);
+ inet6_del_protocol(&ip6gre_protocol, IPPROTO_GRE);
+ unregister_pernet_device(&ip6gre_net_ops);
+}
+
+module_init(ip6gre_init);
+module_exit(ip6gre_fini);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
+MODULE_DESCRIPTION("GRE over IPv6 tunneling device");
+MODULE_ALIAS_RTNL_LINK("ip6gre");
+MODULE_ALIAS_NETDEV("ip6gre0");
+
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
* Re: [PATCH] vxge: Declare MODULE_FIRMWARE usage
From: Tim Gardner @ 2012-07-27 13:06 UTC (permalink / raw)
To: Jon Mason
Cc: linux-kernel, David S. Miller, Joe Perches, Jiri Pirko,
Stephen Hemminger, Paul Gortmaker, netdev
In-Reply-To: <CAPoiz9xoMF23FQOJjJviZjA-Db6Bhc9iQLPHTpFNX1USF4Oazw@mail.gmail.com>
On 07/26/2012 01:55 PM, Jon Mason wrote:
> On Thu, Jul 26, 2012 at 12:08 PM, Tim Gardner <tim.gardner@canonical.com> wrote:
>> Cc: Jon Mason <jdmason@kudzu.us>
>> Cc: "David S. Miller" <davem@davemloft.net>
>> Cc: Joe Perches <joe@perches.com>
>> Cc: Jiri Pirko <jpirko@redhat.com>
>> Cc: Stephen Hemminger <shemminger@vyatta.com>
>> Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
>> Cc: netdev@vger.kernel.org
>> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
>> ---
>> drivers/net/ethernet/neterion/vxge/vxge-main.c | 9 +++++++--
>> 1 file changed, 7 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
>> index de21904..d4832b2 100644
>> --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
>> +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
>> @@ -4203,6 +4203,9 @@ out:
>> return ret;
>> }
>>
>> +#define VXGE_PXE_FIRMWARE "vxge/X3fw-pxe.ncf"
>> +#define VXGE_FIRMWARE "vxge/X3fw.ncf"
>> +
>> static int vxge_probe_fw_update(struct vxgedev *vdev)
>> {
>> u32 maj, min, bld;
>> @@ -4245,9 +4248,9 @@ static int vxge_probe_fw_update(struct vxgedev *vdev)
>> }
>> }
>> if (gpxe)
>> - fw_name = "vxge/X3fw-pxe.ncf";
>> + fw_name = VXGE_PXE_FIRMWARE;
>> else
>> - fw_name = "vxge/X3fw.ncf";
>> + fw_name = VXGE_FIRMWARE;
>>
>> ret = vxge_fw_upgrade(vdev, fw_name, 0);
>> /* -EINVAL and -ENOENT are not fatal errors for flashing firmware on
>> @@ -4855,3 +4858,5 @@ vxge_closer(void)
>> }
>> module_init(vxge_starter);
>> module_exit(vxge_closer);
>> +MODULE_FIRMWARE(VXGE_PXE_FIRMWARE);
>> +MODULE_FIRMWARE(VXGE_FIRMWARE);
>
> IIUC, MODULE_FIRMWARE is only necessary for devices that need firmware
> to operate. vxge hardware has an image in flash on the nic, and the
> modified code is used to update the firmware image on the adapter.
> So, this change isn't doing what you want it to do.
>
> Also, wasn't this already discussed (https://lkml.org/lkml/2012/4/12/401)?
>
> Thanks,
> Jon
>
Hmm, I'd forgotten about that discussion, but its no more correct now
then it was then. MODULE_FIRMWARE is purely informational and has _no_
runtime impact. It is merely an indicator that a firmware file _might_
be used by the kernel and should therefore be left in the external
firmware package. MODULE_FIRMWARE() uses the same base MODULE_INFO macro
as MODULE_VERSION, MODULE_AUTHOR, MODULE_LICENSE, etc.
rtg
--
Tim Gardner tim.gardner@canonical.com
^ 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