2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2007-02-09 21:24:47 +07:00
|
|
|
* Linux NET3: IP/IP protocol decoder.
|
2005-04-17 05:20:36 +07:00
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
|
|
|
|
*
|
|
|
|
* Fixes:
|
|
|
|
* Alan Cox : Merged and made usable non modular (its so tiny its silly as
|
|
|
|
* a module taking up 2 pages).
|
|
|
|
* Alan Cox : Fixed bug with 1.3.18 and IPIP not working (now needs to set skb->h.iph)
|
|
|
|
* to keep ip_forward happy.
|
|
|
|
* Alan Cox : More fixes for 1.3.21, and firewall fix. Maybe this will work soon 8).
|
|
|
|
* Kai Schulte : Fixed #defines for IP_FIREWALL->FIREWALL
|
|
|
|
* David Woodhouse : Perform some basic ICMP handling.
|
|
|
|
* IPIP Routing without decapsulation.
|
|
|
|
* Carlos Picoto : GRE over IP support
|
|
|
|
* Alexey Kuznetsov: Reworked. Really, now it is truncated version of ipv4/ip_gre.c.
|
|
|
|
* I do not want to merge them together.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* tunnel.c: an IP tunnel driver
|
|
|
|
|
|
|
|
The purpose of this driver is to provide an IP tunnel through
|
|
|
|
which you can tunnel network traffic transparently across subnets.
|
|
|
|
|
|
|
|
This was written by looking at Nick Holloway's dummy driver
|
|
|
|
Thanks for the great code!
|
|
|
|
|
|
|
|
-Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
Minor tweaks:
|
|
|
|
Cleaned up the code a little and added some pre-1.3.0 tweaks.
|
|
|
|
dev->hard_header/hard_header_len changed to use no headers.
|
|
|
|
Comments/bracketing tweaked.
|
|
|
|
Made the tunnels use dev->name not tunnel: when error reporting.
|
|
|
|
Added tx_dropped stat
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2008-10-14 09:01:08 +07:00
|
|
|
-Alan Cox (alan@lxorguk.ukuu.org.uk) 21 March 95
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
Reworked:
|
|
|
|
Changed to tunnel to destination gateway in addition to the
|
|
|
|
tunnel's pointopoint address
|
|
|
|
Almost completely rewritten
|
|
|
|
Note: There is currently no firewall or ICMP handling done.
|
|
|
|
|
|
|
|
-Sam Lantinga (slouken@cs.ucdavis.edu) 02/13/96
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Things I wish I had known when writing the tunnel driver:
|
|
|
|
|
|
|
|
When the tunnel_xmit() function is called, the skb contains the
|
|
|
|
packet to be sent (plus a great deal of extra info), and dev
|
|
|
|
contains the tunnel device that _we_ are.
|
|
|
|
|
|
|
|
When we are passed a packet, we are expected to fill in the
|
|
|
|
source address with our source IP address.
|
|
|
|
|
|
|
|
What is the proper way to allocate, copy and free a buffer?
|
|
|
|
After you allocate it, it is a "0 length" chunk of memory
|
|
|
|
starting at zero. If you want to add headers to the buffer
|
|
|
|
later, you'll have to call "skb_reserve(skb, amount)" with
|
|
|
|
the amount of memory you want reserved. Then, you call
|
|
|
|
"skb_put(skb, amount)" with the amount of space you want in
|
|
|
|
the buffer. skb_put() returns a pointer to the top (#0) of
|
|
|
|
that buffer. skb->len is set to the amount of space you have
|
|
|
|
"allocated" with skb_put(). You can then write up to skb->len
|
|
|
|
bytes to that buffer. If you need more, you can call skb_put()
|
|
|
|
again with the additional amount of space you need. You can
|
2007-02-09 21:24:47 +07:00
|
|
|
find out how much more space you can allocate by calling
|
2005-04-17 05:20:36 +07:00
|
|
|
"skb_tailroom(skb)".
|
|
|
|
Now, to add header space, call "skb_push(skb, header_len)".
|
|
|
|
This creates space at the beginning of the buffer and returns
|
|
|
|
a pointer to this new space. If later you need to strip a
|
|
|
|
header from a buffer, call "skb_pull(skb, header_len)".
|
|
|
|
skb_headroom() will return how much space is left at the top
|
|
|
|
of the buffer (before the main data). Remember, this headroom
|
|
|
|
space must be reserved before the skb_put() function is called.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
This version of net/ipv4/ipip.c is cloned of net/ipv4/ip_gre.c
|
|
|
|
|
|
|
|
For comments look at net/ipv4/ip_gre.c --ANK
|
|
|
|
*/
|
|
|
|
|
2007-02-09 21:24:47 +07:00
|
|
|
|
2006-01-12 03:17:47 +07:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 15:04:11 +07:00
|
|
|
#include <linux/slab.h>
|
2016-12-25 02:46:01 +07:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#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/init.h>
|
|
|
|
#include <linux/netfilter_ipv4.h>
|
2006-01-06 07:35:42 +07:00
|
|
|
#include <linux/if_ether.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include <net/sock.h>
|
|
|
|
#include <net/ip.h>
|
|
|
|
#include <net/icmp.h>
|
2013-03-25 21:49:35 +07:00
|
|
|
#include <net/ip_tunnels.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <net/inet_ecn.h>
|
|
|
|
#include <net/xfrm.h>
|
2008-04-16 15:03:13 +07:00
|
|
|
#include <net/net_namespace.h>
|
|
|
|
#include <net/netns/generic.h>
|
2016-09-16 03:00:29 +07:00
|
|
|
#include <net/dst_metadata.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-09-25 18:02:48 +07:00
|
|
|
static bool log_ecn_error = true;
|
|
|
|
module_param(log_ecn_error, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
|
|
|
|
|
netns: make struct pernet_operations::id unsigned int
Make struct pernet_operations::id unsigned.
There are 2 reasons to do so:
1)
This field is really an index into an zero based array and
thus is unsigned entity. Using negative value is out-of-bound
access by definition.
2)
On x86_64 unsigned 32-bit data which are mixed with pointers
via array indexing or offsets added or subtracted to pointers
are preffered to signed 32-bit data.
"int" being used as an array index needs to be sign-extended
to 64-bit before being used.
void f(long *p, int i)
{
g(p[i]);
}
roughly translates to
movsx rsi, esi
mov rdi, [rsi+...]
call g
MOVSX is 3 byte instruction which isn't necessary if the variable is
unsigned because x86_64 is zero extending by default.
Now, there is net_generic() function which, you guessed it right, uses
"int" as an array index:
static inline void *net_generic(const struct net *net, int id)
{
...
ptr = ng->ptr[id - 1];
...
}
And this function is used a lot, so those sign extensions add up.
Patch snipes ~1730 bytes on allyesconfig kernel (without all junk
messing with code generation):
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
Unfortunately some functions actually grow bigger.
This is a semmingly random artefact of code generation with register
allocator being used differently. gcc decides that some variable
needs to live in new r8+ registers and every access now requires REX
prefix. Or it is shifted into r12, so [r12+0] addressing mode has to be
used which is longer than [r8]
However, overall balance is in negative direction:
add/remove: 0/0 grow/shrink: 70/598 up/down: 396/-2126 (-1730)
function old new delta
nfsd4_lock 3886 3959 +73
tipc_link_build_proto_msg 1096 1140 +44
mac80211_hwsim_new_radio 2776 2808 +32
tipc_mon_rcv 1032 1058 +26
svcauth_gss_legacy_init 1413 1429 +16
tipc_bcbase_select_primary 379 392 +13
nfsd4_exchange_id 1247 1260 +13
nfsd4_setclientid_confirm 782 793 +11
...
put_client_renew_locked 494 480 -14
ip_set_sockfn_get 730 716 -14
geneve_sock_add 829 813 -16
nfsd4_sequence_done 721 703 -18
nlmclnt_lookup_host 708 686 -22
nfsd4_lockt 1085 1063 -22
nfs_get_client 1077 1050 -27
tcf_bpf_init 1106 1076 -30
nfsd4_encode_fattr 5997 5930 -67
Total: Before=154856051, After=154854321, chg -0.00%
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-11-17 08:58:21 +07:00
|
|
|
static unsigned int ipip_net_id __read_mostly;
|
2008-04-16 15:03:13 +07:00
|
|
|
|
2010-09-27 07:35:50 +07:00
|
|
|
static int ipip_tunnel_init(struct net_device *dev);
|
2012-11-09 13:09:59 +07:00
|
|
|
static struct rtnl_link_ops ipip_link_ops __read_mostly;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-28 16:12:13 +07:00
|
|
|
static int ipip_err(struct sk_buff *skb, u32 info)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
|
2008-05-22 07:47:54 +07:00
|
|
|
/* All the routers (except for Linux) return only
|
2005-04-17 05:20:36 +07:00
|
|
|
8 bytes of packet payload. It means, that precise relaying of
|
|
|
|
ICMP in the real Internet is absolutely infeasible.
|
|
|
|
*/
|
2013-03-25 21:49:41 +07:00
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
|
2011-04-22 11:53:02 +07:00
|
|
|
const struct iphdr *iph = (const struct iphdr *)skb->data;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct ip_tunnel *t;
|
2006-03-28 16:12:13 +07:00
|
|
|
int err;
|
2013-03-25 21:49:41 +07:00
|
|
|
const int type = icmp_hdr(skb)->type;
|
|
|
|
const int code = icmp_hdr(skb)->code;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2006-03-28 16:12:13 +07:00
|
|
|
err = -ENOENT;
|
2013-03-25 21:49:41 +07:00
|
|
|
t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
|
|
|
|
iph->daddr, iph->saddr, 0);
|
2015-04-03 15:17:26 +07:00
|
|
|
if (!t)
|
2012-06-15 12:21:46 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
|
|
|
|
ipv4_update_pmtu(skb, dev_net(skb->dev), info,
|
2016-07-07 12:56:14 +07:00
|
|
|
t->parms.link, 0, iph->protocol, 0);
|
2012-06-15 12:21:46 +07:00
|
|
|
err = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-07-12 11:27:49 +07:00
|
|
|
if (type == ICMP_REDIRECT) {
|
2014-06-07 02:19:21 +07:00
|
|
|
ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
|
2016-07-07 12:56:14 +07:00
|
|
|
iph->protocol, 0);
|
2012-07-12 11:27:49 +07:00
|
|
|
err = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-06-15 12:21:46 +07:00
|
|
|
if (t->parms.iph.daddr == 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto out;
|
2006-03-28 16:12:13 +07:00
|
|
|
|
|
|
|
err = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
|
|
|
|
goto out;
|
|
|
|
|
2009-02-25 14:36:47 +07:00
|
|
|
if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
|
2005-04-17 05:20:36 +07:00
|
|
|
t->err_count++;
|
|
|
|
else
|
|
|
|
t->err_count = 1;
|
|
|
|
t->err_time = jiffies;
|
2012-09-25 01:12:25 +07:00
|
|
|
|
2013-03-25 21:49:41 +07:00
|
|
|
out:
|
2006-03-28 16:12:13 +07:00
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
static const struct tnl_ptk_info ipip_tpi = {
|
2013-03-25 21:49:41 +07:00
|
|
|
/* no tunnel info required for ipip. */
|
|
|
|
.proto = htons(ETH_P_IP),
|
|
|
|
};
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
static const struct tnl_ptk_info mplsip_tpi = {
|
|
|
|
/* no tunnel info required for mplsip. */
|
|
|
|
.proto = htons(ETH_P_MPLS_UC),
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int ipip_tunnel_rcv(struct sk_buff *skb, u8 ipproto)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2013-03-25 21:49:41 +07:00
|
|
|
struct net *net = dev_net(skb->dev);
|
|
|
|
struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
|
2016-09-16 03:00:29 +07:00
|
|
|
struct metadata_dst *tun_dst = NULL;
|
2005-04-17 05:20:36 +07:00
|
|
|
struct ip_tunnel *tunnel;
|
2013-06-18 07:50:02 +07:00
|
|
|
const struct iphdr *iph;
|
2010-09-27 07:35:50 +07:00
|
|
|
|
2013-06-18 07:50:02 +07:00
|
|
|
iph = ip_hdr(skb);
|
2013-03-25 21:49:41 +07:00
|
|
|
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
|
|
|
|
iph->saddr, iph->daddr, 0);
|
|
|
|
if (tunnel) {
|
2016-07-07 12:56:14 +07:00
|
|
|
const struct tnl_ptk_info *tpi;
|
|
|
|
|
|
|
|
if (tunnel->parms.iph.protocol != ipproto &&
|
|
|
|
tunnel->parms.iph.protocol != 0)
|
|
|
|
goto drop;
|
|
|
|
|
2012-09-25 18:02:48 +07:00
|
|
|
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
|
|
|
|
goto drop;
|
2016-07-07 12:56:14 +07:00
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
if (ipproto == IPPROTO_MPLS)
|
|
|
|
tpi = &mplsip_tpi;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
tpi = &ipip_tpi;
|
|
|
|
if (iptunnel_pull_header(skb, 0, tpi->proto, false))
|
2013-08-28 16:54:50 +07:00
|
|
|
goto drop;
|
2016-09-16 03:00:29 +07:00
|
|
|
if (tunnel->collect_md) {
|
|
|
|
tun_dst = ip_tun_rx_dst(skb, 0, 0, 0);
|
|
|
|
if (!tun_dst)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
2012-09-25 18:02:48 +07:00
|
|
|
|
|
|
|
drop:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
static int ipip_rcv(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return ipip_tunnel_rcv(skb, IPPROTO_IPIP);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
static int mplsip_rcv(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
return ipip_tunnel_rcv(skb, IPPROTO_MPLS);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* This function assumes it is being called from dev_queue_xmit()
|
|
|
|
* and that skb is filled properly by that function.
|
|
|
|
*/
|
2016-07-07 12:56:14 +07:00
|
|
|
static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb,
|
|
|
|
struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2006-01-09 13:05:26 +07:00
|
|
|
struct ip_tunnel *tunnel = netdev_priv(dev);
|
2011-04-22 11:53:02 +07:00
|
|
|
const struct iphdr *tiph = &tunnel->parms.iph;
|
2016-07-07 12:56:14 +07:00
|
|
|
u8 ipproto;
|
|
|
|
|
|
|
|
switch (skb->protocol) {
|
|
|
|
case htons(ETH_P_IP):
|
|
|
|
ipproto = IPPROTO_IPIP;
|
|
|
|
break;
|
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
case htons(ETH_P_MPLS_UC):
|
|
|
|
ipproto = IPPROTO_MPLS;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
goto tx_error;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
if (tiph->protocol != ipproto && tiph->protocol != 0)
|
2005-04-17 05:20:36 +07:00
|
|
|
goto tx_error;
|
|
|
|
|
2016-05-18 23:06:10 +07:00
|
|
|
if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4))
|
2016-04-15 02:33:37 +07:00
|
|
|
goto tx_error;
|
2013-03-08 22:12:45 +07:00
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
skb_set_inner_ipproto(skb, ipproto);
|
2014-09-30 10:22:31 +07:00
|
|
|
|
2016-09-16 03:00:29 +07:00
|
|
|
if (tunnel->collect_md)
|
|
|
|
ip_md_tunnel_xmit(skb, dev, ipproto);
|
|
|
|
else
|
|
|
|
ip_tunnel_xmit(skb, dev, tiph, ipproto);
|
2009-06-23 13:03:08 +07:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
tx_error:
|
2014-01-19 09:27:49 +07:00
|
|
|
kfree_skb(skb);
|
2016-04-15 02:33:37 +07:00
|
|
|
|
2013-10-20 01:42:57 +07:00
|
|
|
dev->stats.tx_errors++;
|
2009-06-23 13:03:08 +07:00
|
|
|
return NETDEV_TX_OK;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
static bool ipip_tunnel_ioctl_verify_protocol(u8 ipproto)
|
|
|
|
{
|
|
|
|
switch (ipproto) {
|
|
|
|
case 0:
|
|
|
|
case IPPROTO_IPIP:
|
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
case IPPROTO_MPLS:
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int
|
2013-03-25 21:49:41 +07:00
|
|
|
ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct ip_tunnel_parm p;
|
|
|
|
|
2013-03-25 21:49:41 +07:00
|
|
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
|
|
|
|
return -EFAULT;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2013-07-02 13:49:34 +07:00
|
|
|
if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) {
|
2016-07-07 12:56:14 +07:00
|
|
|
if (p.iph.version != 4 ||
|
|
|
|
!ipip_tunnel_ioctl_verify_protocol(p.iph.protocol) ||
|
2013-07-02 13:49:34 +07:00
|
|
|
p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-05-15 22:58:45 +07:00
|
|
|
p.i_key = p.o_key = 0;
|
|
|
|
p.i_flags = p.o_flags = 0;
|
2013-03-25 21:49:41 +07:00
|
|
|
err = ip_tunnel_ioctl(dev, &p, cmd);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-21 11:33:21 +07:00
|
|
|
static const struct net_device_ops ipip_netdev_ops = {
|
2013-03-25 21:49:41 +07:00
|
|
|
.ndo_init = ipip_tunnel_init,
|
|
|
|
.ndo_uninit = ip_tunnel_uninit,
|
2008-11-21 11:33:21 +07:00
|
|
|
.ndo_start_xmit = ipip_tunnel_xmit,
|
|
|
|
.ndo_do_ioctl = ipip_tunnel_ioctl,
|
2013-03-25 21:49:41 +07:00
|
|
|
.ndo_change_mtu = ip_tunnel_change_mtu,
|
|
|
|
.ndo_get_stats64 = ip_tunnel_get_stats64,
|
2015-04-02 22:07:02 +07:00
|
|
|
.ndo_get_iflink = ip_tunnel_get_iflink,
|
2008-11-21 11:33:21 +07:00
|
|
|
};
|
|
|
|
|
2012-11-08 16:59:52 +07:00
|
|
|
#define IPIP_FEATURES (NETIF_F_SG | \
|
|
|
|
NETIF_F_FRAGLIST | \
|
|
|
|
NETIF_F_HIGHDMA | \
|
2013-10-20 01:42:57 +07:00
|
|
|
NETIF_F_GSO_SOFTWARE | \
|
2012-11-08 16:59:52 +07:00
|
|
|
NETIF_F_HW_CSUM)
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static void ipip_tunnel_setup(struct net_device *dev)
|
|
|
|
{
|
2008-11-21 11:33:21 +07:00
|
|
|
dev->netdev_ops = &ipip_netdev_ops;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
dev->type = ARPHRD_TUNNEL;
|
|
|
|
dev->flags = IFF_NOARP;
|
|
|
|
dev->addr_len = 4;
|
2010-09-28 07:17:17 +07:00
|
|
|
dev->features |= NETIF_F_LLTX;
|
2014-10-06 08:38:35 +07:00
|
|
|
netif_keep_dst(dev);
|
2012-11-08 16:59:52 +07:00
|
|
|
|
|
|
|
dev->features |= IPIP_FEATURES;
|
|
|
|
dev->hw_features |= IPIP_FEATURES;
|
2013-03-25 21:49:41 +07:00
|
|
|
ip_tunnel_setup(dev, ipip_net_id);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2010-09-27 07:35:50 +07:00
|
|
|
static int ipip_tunnel_init(struct net_device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2008-11-21 11:33:21 +07:00
|
|
|
struct ip_tunnel *tunnel = netdev_priv(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
|
|
|
|
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
|
|
|
|
|
2014-09-18 02:26:00 +07:00
|
|
|
tunnel->tun_hlen = 0;
|
|
|
|
tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen;
|
2013-03-25 21:49:41 +07:00
|
|
|
return ip_tunnel_init(dev);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2017-06-26 04:56:01 +07:00
|
|
|
static int ipip_tunnel_validate(struct nlattr *tb[], struct nlattr *data[],
|
|
|
|
struct netlink_ext_ack *extack)
|
2016-07-07 12:56:14 +07:00
|
|
|
{
|
|
|
|
u8 proto;
|
|
|
|
|
|
|
|
if (!data || !data[IFLA_IPTUN_PROTO])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
|
|
|
|
if (proto != IPPROTO_IPIP && proto != IPPROTO_MPLS && proto != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-14 12:14:03 +07:00
|
|
|
static void ipip_netlink_parms(struct nlattr *data[],
|
2017-04-19 23:30:54 +07:00
|
|
|
struct ip_tunnel_parm *parms, bool *collect_md,
|
|
|
|
__u32 *fwmark)
|
2012-11-14 12:14:03 +07:00
|
|
|
{
|
|
|
|
memset(parms, 0, sizeof(*parms));
|
|
|
|
|
|
|
|
parms->iph.version = 4;
|
|
|
|
parms->iph.protocol = IPPROTO_IPIP;
|
|
|
|
parms->iph.ihl = 5;
|
2016-09-16 03:00:29 +07:00
|
|
|
*collect_md = false;
|
2012-11-14 12:14:03 +07:00
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_LINK])
|
|
|
|
parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_LOCAL])
|
2015-03-29 21:59:26 +07:00
|
|
|
parms->iph.saddr = nla_get_in_addr(data[IFLA_IPTUN_LOCAL]);
|
2012-11-14 12:14:03 +07:00
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_REMOTE])
|
2015-03-29 21:59:26 +07:00
|
|
|
parms->iph.daddr = nla_get_in_addr(data[IFLA_IPTUN_REMOTE]);
|
2012-11-14 12:14:03 +07:00
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_TTL]) {
|
|
|
|
parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
|
|
|
|
if (parms->iph.ttl)
|
|
|
|
parms->iph.frag_off = htons(IP_DF);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_TOS])
|
|
|
|
parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
if (data[IFLA_IPTUN_PROTO])
|
|
|
|
parms->iph.protocol = nla_get_u8(data[IFLA_IPTUN_PROTO]);
|
|
|
|
|
2012-11-14 12:14:03 +07:00
|
|
|
if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
|
|
|
|
parms->iph.frag_off = htons(IP_DF);
|
2016-09-16 03:00:29 +07:00
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_COLLECT_METADATA])
|
|
|
|
*collect_md = true;
|
2017-04-19 23:30:54 +07:00
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_FWMARK])
|
|
|
|
*fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]);
|
2012-11-14 12:14:03 +07:00
|
|
|
}
|
|
|
|
|
2014-09-18 02:26:00 +07:00
|
|
|
/* This function returns true when ENCAP attributes are present in the nl msg */
|
|
|
|
static bool ipip_netlink_encap_parms(struct nlattr *data[],
|
|
|
|
struct ip_tunnel_encap *ipencap)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
memset(ipencap, 0, sizeof(*ipencap));
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_ENCAP_TYPE]) {
|
|
|
|
ret = true;
|
|
|
|
ipencap->type = nla_get_u16(data[IFLA_IPTUN_ENCAP_TYPE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_ENCAP_FLAGS]) {
|
|
|
|
ret = true;
|
|
|
|
ipencap->flags = nla_get_u16(data[IFLA_IPTUN_ENCAP_FLAGS]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_ENCAP_SPORT]) {
|
|
|
|
ret = true;
|
2015-02-06 23:22:22 +07:00
|
|
|
ipencap->sport = nla_get_be16(data[IFLA_IPTUN_ENCAP_SPORT]);
|
2014-09-18 02:26:00 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (data[IFLA_IPTUN_ENCAP_DPORT]) {
|
|
|
|
ret = true;
|
2015-02-06 23:22:22 +07:00
|
|
|
ipencap->dport = nla_get_be16(data[IFLA_IPTUN_ENCAP_DPORT]);
|
2014-09-18 02:26:00 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-11-14 12:14:03 +07:00
|
|
|
static int ipip_newlink(struct net *src_net, struct net_device *dev,
|
2017-06-26 04:55:59 +07:00
|
|
|
struct nlattr *tb[], struct nlattr *data[],
|
|
|
|
struct netlink_ext_ack *extack)
|
2012-11-14 12:14:03 +07:00
|
|
|
{
|
2016-09-16 03:00:29 +07:00
|
|
|
struct ip_tunnel *t = netdev_priv(dev);
|
2013-03-25 21:49:41 +07:00
|
|
|
struct ip_tunnel_parm p;
|
2014-09-18 02:26:00 +07:00
|
|
|
struct ip_tunnel_encap ipencap;
|
2017-04-19 23:30:54 +07:00
|
|
|
__u32 fwmark = 0;
|
2014-09-18 02:26:00 +07:00
|
|
|
|
|
|
|
if (ipip_netlink_encap_parms(data, &ipencap)) {
|
|
|
|
int err = ip_tunnel_encap_setup(t, &ipencap);
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
2012-11-14 12:14:03 +07:00
|
|
|
|
2017-04-19 23:30:54 +07:00
|
|
|
ipip_netlink_parms(data, &p, &t->collect_md, &fwmark);
|
|
|
|
return ip_tunnel_newlink(dev, tb, &p, fwmark);
|
2012-11-14 12:14:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
|
2017-06-26 04:56:00 +07:00
|
|
|
struct nlattr *data[],
|
|
|
|
struct netlink_ext_ack *extack)
|
2012-11-14 12:14:03 +07:00
|
|
|
{
|
2017-04-19 23:30:54 +07:00
|
|
|
struct ip_tunnel *t = netdev_priv(dev);
|
2012-11-14 12:14:03 +07:00
|
|
|
struct ip_tunnel_parm p;
|
2014-09-18 02:26:00 +07:00
|
|
|
struct ip_tunnel_encap ipencap;
|
2016-09-16 03:00:29 +07:00
|
|
|
bool collect_md;
|
2017-04-19 23:30:54 +07:00
|
|
|
__u32 fwmark = t->fwmark;
|
2014-09-18 02:26:00 +07:00
|
|
|
|
|
|
|
if (ipip_netlink_encap_parms(data, &ipencap)) {
|
|
|
|
int err = ip_tunnel_encap_setup(t, &ipencap);
|
|
|
|
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
2012-11-14 12:14:03 +07:00
|
|
|
|
2017-04-19 23:30:54 +07:00
|
|
|
ipip_netlink_parms(data, &p, &collect_md, &fwmark);
|
2016-09-16 03:00:29 +07:00
|
|
|
if (collect_md)
|
|
|
|
return -EINVAL;
|
2012-11-14 12:14:03 +07:00
|
|
|
|
|
|
|
if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
|
|
|
|
(!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-04-19 23:30:54 +07:00
|
|
|
return ip_tunnel_changelink(dev, tb, &p, fwmark);
|
2012-11-14 12:14:03 +07:00
|
|
|
}
|
|
|
|
|
2012-11-09 13:09:59 +07:00
|
|
|
static size_t ipip_get_size(const struct net_device *dev)
|
|
|
|
{
|
|
|
|
return
|
|
|
|
/* IFLA_IPTUN_LINK */
|
|
|
|
nla_total_size(4) +
|
|
|
|
/* IFLA_IPTUN_LOCAL */
|
|
|
|
nla_total_size(4) +
|
|
|
|
/* IFLA_IPTUN_REMOTE */
|
|
|
|
nla_total_size(4) +
|
|
|
|
/* IFLA_IPTUN_TTL */
|
|
|
|
nla_total_size(1) +
|
|
|
|
/* IFLA_IPTUN_TOS */
|
|
|
|
nla_total_size(1) +
|
2016-07-07 12:56:14 +07:00
|
|
|
/* IFLA_IPTUN_PROTO */
|
|
|
|
nla_total_size(1) +
|
2012-11-14 12:14:02 +07:00
|
|
|
/* IFLA_IPTUN_PMTUDISC */
|
|
|
|
nla_total_size(1) +
|
2014-09-18 02:26:00 +07:00
|
|
|
/* IFLA_IPTUN_ENCAP_TYPE */
|
|
|
|
nla_total_size(2) +
|
|
|
|
/* IFLA_IPTUN_ENCAP_FLAGS */
|
|
|
|
nla_total_size(2) +
|
|
|
|
/* IFLA_IPTUN_ENCAP_SPORT */
|
|
|
|
nla_total_size(2) +
|
|
|
|
/* IFLA_IPTUN_ENCAP_DPORT */
|
|
|
|
nla_total_size(2) +
|
2016-09-16 03:00:29 +07:00
|
|
|
/* IFLA_IPTUN_COLLECT_METADATA */
|
|
|
|
nla_total_size(0) +
|
2017-04-19 23:30:54 +07:00
|
|
|
/* IFLA_IPTUN_FWMARK */
|
|
|
|
nla_total_size(4) +
|
2012-11-09 13:09:59 +07:00
|
|
|
0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct ip_tunnel *tunnel = netdev_priv(dev);
|
|
|
|
struct ip_tunnel_parm *parm = &tunnel->parms;
|
|
|
|
|
|
|
|
if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
|
2015-03-29 21:59:25 +07:00
|
|
|
nla_put_in_addr(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
|
|
|
|
nla_put_in_addr(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
|
2012-11-09 13:09:59 +07:00
|
|
|
nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
|
2012-11-14 12:14:02 +07:00
|
|
|
nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
|
2016-07-07 12:56:14 +07:00
|
|
|
nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) ||
|
2012-11-14 12:14:02 +07:00
|
|
|
nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
|
2017-04-19 23:30:54 +07:00
|
|
|
!!(parm->iph.frag_off & htons(IP_DF))) ||
|
|
|
|
nla_put_u32(skb, IFLA_IPTUN_FWMARK, tunnel->fwmark))
|
2012-11-09 13:09:59 +07:00
|
|
|
goto nla_put_failure;
|
2014-09-18 02:26:00 +07:00
|
|
|
|
|
|
|
if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE,
|
|
|
|
tunnel->encap.type) ||
|
2015-02-06 23:22:22 +07:00
|
|
|
nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT,
|
|
|
|
tunnel->encap.sport) ||
|
|
|
|
nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT,
|
|
|
|
tunnel->encap.dport) ||
|
2014-09-18 02:26:00 +07:00
|
|
|
nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
|
2014-11-06 07:49:38 +07:00
|
|
|
tunnel->encap.flags))
|
2014-09-18 02:26:00 +07:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2016-09-16 03:00:29 +07:00
|
|
|
if (tunnel->collect_md)
|
|
|
|
if (nla_put_flag(skb, IFLA_IPTUN_COLLECT_METADATA))
|
|
|
|
goto nla_put_failure;
|
2012-11-09 13:09:59 +07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
2012-11-14 12:14:03 +07:00
|
|
|
static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
|
|
|
|
[IFLA_IPTUN_LINK] = { .type = NLA_U32 },
|
|
|
|
[IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
|
|
|
|
[IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
|
|
|
|
[IFLA_IPTUN_TTL] = { .type = NLA_U8 },
|
|
|
|
[IFLA_IPTUN_TOS] = { .type = NLA_U8 },
|
2016-07-07 12:56:14 +07:00
|
|
|
[IFLA_IPTUN_PROTO] = { .type = NLA_U8 },
|
2012-11-14 12:14:03 +07:00
|
|
|
[IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
|
2014-09-18 02:26:00 +07:00
|
|
|
[IFLA_IPTUN_ENCAP_TYPE] = { .type = NLA_U16 },
|
|
|
|
[IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 },
|
|
|
|
[IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 },
|
|
|
|
[IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 },
|
2016-09-16 03:00:29 +07:00
|
|
|
[IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG },
|
2017-04-19 23:30:54 +07:00
|
|
|
[IFLA_IPTUN_FWMARK] = { .type = NLA_U32 },
|
2012-11-14 12:14:03 +07:00
|
|
|
};
|
|
|
|
|
2012-11-09 13:09:59 +07:00
|
|
|
static struct rtnl_link_ops ipip_link_ops __read_mostly = {
|
|
|
|
.kind = "ipip",
|
|
|
|
.maxtype = IFLA_IPTUN_MAX,
|
2012-11-14 12:14:03 +07:00
|
|
|
.policy = ipip_policy,
|
2012-11-09 13:09:59 +07:00
|
|
|
.priv_size = sizeof(struct ip_tunnel),
|
2012-11-14 12:14:03 +07:00
|
|
|
.setup = ipip_tunnel_setup,
|
2016-07-07 12:56:14 +07:00
|
|
|
.validate = ipip_tunnel_validate,
|
2012-11-14 12:14:03 +07:00
|
|
|
.newlink = ipip_newlink,
|
|
|
|
.changelink = ipip_changelink,
|
2013-03-25 21:49:41 +07:00
|
|
|
.dellink = ip_tunnel_dellink,
|
2012-11-09 13:09:59 +07:00
|
|
|
.get_size = ipip_get_size,
|
|
|
|
.fill_info = ipip_fill_info,
|
2015-01-15 21:11:17 +07:00
|
|
|
.get_link_net = ip_tunnel_get_link_net,
|
2012-11-09 13:09:59 +07:00
|
|
|
};
|
|
|
|
|
2010-08-30 14:04:14 +07:00
|
|
|
static struct xfrm_tunnel ipip_handler __read_mostly = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.handler = ipip_rcv,
|
|
|
|
.err_handler = ipip_err,
|
2006-03-28 16:12:13 +07:00
|
|
|
.priority = 1,
|
2005-04-17 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
static struct xfrm_tunnel mplsip_handler __read_mostly = {
|
|
|
|
.handler = mplsip_rcv,
|
|
|
|
.err_handler = ipip_err,
|
|
|
|
.priority = 1,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2010-01-17 10:35:32 +07:00
|
|
|
static int __net_init ipip_init_net(struct net *net)
|
2008-04-16 15:03:13 +07:00
|
|
|
{
|
2013-03-25 21:49:41 +07:00
|
|
|
return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0");
|
2008-04-16 15:03:13 +07:00
|
|
|
}
|
|
|
|
|
2010-01-17 10:35:32 +07:00
|
|
|
static void __net_exit ipip_exit_net(struct net *net)
|
2008-04-16 15:03:13 +07:00
|
|
|
{
|
2013-03-25 21:49:41 +07:00
|
|
|
struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
|
2013-08-13 22:51:11 +07:00
|
|
|
ip_tunnel_delete_net(itn, &ipip_link_ops);
|
2008-04-16 15:03:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations ipip_net_ops = {
|
|
|
|
.init = ipip_init_net,
|
|
|
|
.exit = ipip_exit_net,
|
2009-11-29 22:46:14 +07:00
|
|
|
.id = &ipip_net_id,
|
2013-03-25 21:49:41 +07:00
|
|
|
.size = sizeof(struct ip_tunnel_net),
|
2008-04-16 15:03:13 +07:00
|
|
|
};
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
static int __init ipip_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2016-07-07 12:56:14 +07:00
|
|
|
pr_info("ipip: IPv4 and MPLS over IPv4 tunneling driver\n");
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2010-02-16 16:05:04 +07:00
|
|
|
err = register_pernet_device(&ipip_net_ops);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
|
|
|
|
if (err < 0) {
|
2012-03-12 01:36:11 +07:00
|
|
|
pr_info("%s: can't register tunnel\n", __func__);
|
2016-07-07 12:56:14 +07:00
|
|
|
goto xfrm_tunnel_ipip_failed;
|
|
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
err = xfrm4_tunnel_register(&mplsip_handler, AF_MPLS);
|
|
|
|
if (err < 0) {
|
|
|
|
pr_info("%s: can't register tunnel\n", __func__);
|
|
|
|
goto xfrm_tunnel_mplsip_failed;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2016-07-07 12:56:14 +07:00
|
|
|
#endif
|
2012-11-09 13:09:59 +07:00
|
|
|
err = rtnl_link_register(&ipip_link_ops);
|
|
|
|
if (err < 0)
|
|
|
|
goto rtnl_link_failed;
|
|
|
|
|
|
|
|
out:
|
2005-04-17 05:20:36 +07:00
|
|
|
return err;
|
2012-11-09 13:09:59 +07:00
|
|
|
|
|
|
|
rtnl_link_failed:
|
2016-07-07 12:56:14 +07:00
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
xfrm4_tunnel_deregister(&mplsip_handler, AF_INET);
|
|
|
|
xfrm_tunnel_mplsip_failed:
|
|
|
|
|
|
|
|
#endif
|
2012-11-09 13:09:59 +07:00
|
|
|
xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
|
2016-07-07 12:56:14 +07:00
|
|
|
xfrm_tunnel_ipip_failed:
|
2012-11-09 13:09:59 +07:00
|
|
|
unregister_pernet_device(&ipip_net_ops);
|
|
|
|
goto out;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ipip_fini(void)
|
|
|
|
{
|
2012-11-09 13:09:59 +07:00
|
|
|
rtnl_link_unregister(&ipip_link_ops);
|
2007-02-14 03:54:47 +07:00
|
|
|
if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
|
2012-03-12 01:36:11 +07:00
|
|
|
pr_info("%s: can't deregister tunnel\n", __func__);
|
2016-07-07 12:56:14 +07:00
|
|
|
#if IS_ENABLED(CONFIG_MPLS)
|
|
|
|
if (xfrm4_tunnel_deregister(&mplsip_handler, AF_MPLS))
|
|
|
|
pr_info("%s: can't deregister tunnel\n", __func__);
|
|
|
|
#endif
|
2009-11-29 22:46:14 +07:00
|
|
|
unregister_pernet_device(&ipip_net_ops);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ipip_init);
|
|
|
|
module_exit(ipip_fini);
|
|
|
|
MODULE_LICENSE("GPL");
|
2014-05-16 04:21:30 +07:00
|
|
|
MODULE_ALIAS_RTNL_LINK("ipip");
|
2011-03-02 04:33:13 +07:00
|
|
|
MODULE_ALIAS_NETDEV("tunl0");
|