1672 lines
47 KiB
C
1672 lines
47 KiB
C
/*
|
|
* IPSEC Tunneling code. Heavily based on drivers/net/new_tunnel.c
|
|
* Copyright (C) 1996, 1997 John Ioannidis.
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs.
|
|
*
|
|
* 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. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
char ipsec_tunnel_c_version[] = "RCSID $Id: ipsec_tunnel.c,v 1.4 2005/06/16 21:21:02 as Exp $";
|
|
|
|
#define __NO_VERSION__
|
|
#include <linux/module.h>
|
|
#include <linux/config.h> /* for CONFIG_IP_FORWARD */
|
|
#include <linux/version.h>
|
|
#include <linux/kernel.h> /* printk() */
|
|
|
|
#include "freeswan/ipsec_param.h"
|
|
|
|
#ifdef MALLOC_SLAB
|
|
# include <linux/slab.h> /* kmalloc() */
|
|
#else /* MALLOC_SLAB */
|
|
# include <linux/malloc.h> /* kmalloc() */
|
|
#endif /* MALLOC_SLAB */
|
|
#include <linux/errno.h> /* error codes */
|
|
#include <linux/types.h> /* size_t */
|
|
#include <linux/interrupt.h> /* mark_bh */
|
|
|
|
#include <linux/netdevice.h> /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */
|
|
#include <linux/etherdevice.h> /* eth_type_trans */
|
|
#include <linux/ip.h> /* struct iphdr */
|
|
#include <linux/tcp.h> /* struct tcphdr */
|
|
#include <linux/udp.h> /* struct udphdr */
|
|
#include <linux/skbuff.h>
|
|
#include <freeswan.h>
|
|
#ifdef NET_21
|
|
# include <asm/uaccess.h>
|
|
# include <linux/in6.h>
|
|
# define ip_chk_addr inet_addr_type
|
|
# define IS_MYADDR RTN_LOCAL
|
|
# include <net/dst.h>
|
|
# undef dev_kfree_skb
|
|
# define dev_kfree_skb(a,b) kfree_skb(a)
|
|
# define PHYSDEV_TYPE
|
|
#endif /* NET_21 */
|
|
#include <asm/checksum.h>
|
|
#include <net/icmp.h> /* icmp_send() */
|
|
#include <net/ip.h>
|
|
#ifdef NETDEV_23
|
|
# include <linux/netfilter_ipv4.h>
|
|
#endif /* NETDEV_23 */
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include "freeswan/radij.h"
|
|
#include "freeswan/ipsec_life.h"
|
|
#include "freeswan/ipsec_xform.h"
|
|
#include "freeswan/ipsec_eroute.h"
|
|
#include "freeswan/ipsec_encap.h"
|
|
#include "freeswan/ipsec_radij.h"
|
|
#include "freeswan/ipsec_sa.h"
|
|
#include "freeswan/ipsec_tunnel.h"
|
|
#include "freeswan/ipsec_xmit.h"
|
|
#include "freeswan/ipsec_ipe4.h"
|
|
#include "freeswan/ipsec_ah.h"
|
|
#include "freeswan/ipsec_esp.h"
|
|
|
|
#include <pfkeyv2.h>
|
|
#include <pfkey.h>
|
|
|
|
#include "freeswan/ipsec_proto.h"
|
|
#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
|
|
#include <linux/udp.h>
|
|
#endif
|
|
|
|
static __u32 zeroes[64];
|
|
|
|
#ifdef CONFIG_IPSEC_DEBUG
|
|
int debug_tunnel = 0;
|
|
#endif /* CONFIG_IPSEC_DEBUG */
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_open(struct device *dev)
|
|
{
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
/*
|
|
* Can't open until attached.
|
|
*/
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_open: "
|
|
"dev = %s, prv->dev = %s\n",
|
|
dev->name, prv->dev?prv->dev->name:"NONE");
|
|
|
|
if (prv->dev == NULL)
|
|
return -ENODEV;
|
|
|
|
MOD_INC_USE_COUNT;
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_close(struct device *dev)
|
|
{
|
|
MOD_DEC_USE_COUNT;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NETDEV_23
|
|
static inline int ipsec_tunnel_xmit2(struct sk_buff *skb)
|
|
{
|
|
#ifdef NETDEV_25 /* 2.6 kernels */
|
|
return dst_output(skb);
|
|
#else
|
|
return ip_send(skb);
|
|
#endif
|
|
}
|
|
#endif /* NETDEV_23 */
|
|
|
|
enum ipsec_xmit_value
|
|
ipsec_tunnel_strip_hard_header(struct ipsec_xmit_state *ixs)
|
|
{
|
|
/* ixs->physdev->hard_header_len is unreliable and should not be used */
|
|
ixs->hard_header_len = (unsigned char *)(ixs->iph) - ixs->skb->data;
|
|
|
|
if(ixs->hard_header_len < 0) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_error:ipsec_xmit_strip_hard_header: "
|
|
"Negative hard_header_len (%d)?!\n", ixs->hard_header_len);
|
|
ixs->stats->tx_dropped++;
|
|
return IPSEC_XMIT_BADHHLEN;
|
|
}
|
|
|
|
/* while ixs->physdev->hard_header_len is unreliable and
|
|
* should not be trusted, it accurate and required for ATM, GRE and
|
|
* some other interfaces to work. Thanks to Willy Tarreau
|
|
* <willy@w.ods.org>.
|
|
*/
|
|
if(ixs->hard_header_len == 0) { /* no hard header present */
|
|
ixs->hard_header_stripped = 1;
|
|
ixs->hard_header_len = ixs->physdev->hard_header_len;
|
|
}
|
|
|
|
#ifdef CONFIG_IPSEC_DEBUG
|
|
if (debug_tunnel & DB_TN_XMIT) {
|
|
int i;
|
|
char c;
|
|
|
|
printk(KERN_INFO "klips_debug:ipsec_xmit_strip_hard_header: "
|
|
">>> skb->len=%ld hard_header_len:%d",
|
|
(unsigned long int)ixs->skb->len, ixs->hard_header_len);
|
|
c = ' ';
|
|
for (i=0; i < ixs->hard_header_len; i++) {
|
|
printk("%c%02x", c, ixs->skb->data[i]);
|
|
c = ':';
|
|
}
|
|
printk(" \n");
|
|
}
|
|
#endif /* CONFIG_IPSEC_DEBUG */
|
|
|
|
KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph);
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
|
|
"klips_debug:ipsec_xmit_strip_hard_header: "
|
|
"Original head,tailroom: %d,%d\n",
|
|
skb_headroom(ixs->skb), skb_tailroom(ixs->skb));
|
|
|
|
return IPSEC_XMIT_OK;
|
|
}
|
|
|
|
enum ipsec_xmit_value
|
|
ipsec_tunnel_SAlookup(struct ipsec_xmit_state *ixs)
|
|
{
|
|
/*
|
|
* First things first -- look us up in the erouting tables.
|
|
*/
|
|
ixs->matcher.sen_len = sizeof (struct sockaddr_encap);
|
|
ixs->matcher.sen_family = AF_ENCAP;
|
|
ixs->matcher.sen_type = SENT_IP4;
|
|
ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
|
|
ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
|
|
ixs->matcher.sen_proto = ixs->iph->protocol;
|
|
ipsec_extract_ports(ixs->iph, &ixs->matcher);
|
|
|
|
/*
|
|
* The spinlock is to prevent any other process from accessing or deleting
|
|
* the eroute while we are using and updating it.
|
|
*/
|
|
spin_lock(&eroute_lock);
|
|
|
|
ixs->eroute = ipsec_findroute(&ixs->matcher);
|
|
|
|
if(ixs->iph->protocol == IPPROTO_UDP) {
|
|
if(ixs->skb->sk) {
|
|
ixs->sport=ntohs(ixs->skb->sk->sport);
|
|
ixs->dport=ntohs(ixs->skb->sk->dport);
|
|
} else if((ntohs(ixs->iph->frag_off) & IP_OFFSET) == 0 &&
|
|
((ixs->skb->len - ixs->hard_header_len) >=
|
|
((ixs->iph->ihl << 2) + sizeof(struct udphdr)))) {
|
|
ixs->sport=ntohs(((struct udphdr*)((caddr_t)ixs->iph+(ixs->iph->ihl<<2)))->source);
|
|
ixs->dport=ntohs(((struct udphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl<<2)))->dest);
|
|
} else {
|
|
ixs->sport=0; ixs->dport=0;
|
|
}
|
|
}
|
|
|
|
/* default to a %drop eroute */
|
|
ixs->outgoing_said.proto = IPPROTO_INT;
|
|
ixs->outgoing_said.spi = htonl(SPI_DROP);
|
|
ixs->outgoing_said.dst.s_addr = INADDR_ANY;
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_SAlookup: "
|
|
"checking for local udp/500 IKE packet "
|
|
"saddr=%x, er=0p%p, daddr=%x, er_dst=%x, proto=%d sport=%d dport=%d\n",
|
|
ntohl((unsigned int)ixs->iph->saddr),
|
|
ixs->eroute,
|
|
ntohl((unsigned int)ixs->iph->daddr),
|
|
ixs->eroute ? ntohl((unsigned int)ixs->eroute->er_said.dst.s_addr) : 0,
|
|
ixs->iph->protocol,
|
|
ixs->sport,
|
|
ixs->dport);
|
|
|
|
/*
|
|
* Quick cheat for now...are we udp/500 or udp/4500? If so, let it through
|
|
* without interference since it is most likely an IKE packet.
|
|
*/
|
|
|
|
if (ip_chk_addr((unsigned long)ixs->iph->saddr) == IS_MYADDR
|
|
&& (!ixs->eroute
|
|
|| ixs->iph->daddr == ixs->eroute->er_said.dst.s_addr
|
|
|| INADDR_ANY == ixs->eroute->er_said.dst.s_addr)
|
|
&& ((ixs->sport == 500) || (ixs->sport == 4500))) {
|
|
/* Whatever the eroute, this is an IKE message
|
|
* from us (i.e. not being forwarded).
|
|
* Furthermore, if there is a tunnel eroute,
|
|
* the destination is the peer for this eroute.
|
|
* So %pass the packet: modify the default %drop.
|
|
*/
|
|
ixs->outgoing_said.spi = htonl(SPI_PASS);
|
|
if(!(ixs->skb->sk) && ((ntohs(ixs->iph->frag_off) & IP_MF) != 0)) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_SAlookup: "
|
|
"local UDP/500 (probably IKE) passthrough: base fragment, rest of fragments will probably get filtered.\n");
|
|
}
|
|
} else if (ixs->eroute) {
|
|
ixs->eroute->er_count++;
|
|
ixs->eroute->er_lasttime = jiffies/HZ;
|
|
if(ixs->eroute->er_said.proto==IPPROTO_INT
|
|
&& ixs->eroute->er_said.spi==htonl(SPI_HOLD)) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_SAlookup: "
|
|
"shunt SA of HOLD: skb stored in HOLD.\n");
|
|
if(ixs->eroute->er_last != NULL) {
|
|
kfree_skb(ixs->eroute->er_last);
|
|
}
|
|
ixs->eroute->er_last = ixs->skb;
|
|
ixs->skb = NULL;
|
|
ixs->stats->tx_dropped++;
|
|
spin_unlock(&eroute_lock);
|
|
return IPSEC_XMIT_STOLEN;
|
|
}
|
|
ixs->outgoing_said = ixs->eroute->er_said;
|
|
ixs->eroute_pid = ixs->eroute->er_pid;
|
|
/* Copy of the ident for the TRAP/TRAPSUBNET eroutes */
|
|
if(ixs->outgoing_said.proto==IPPROTO_INT
|
|
&& (ixs->outgoing_said.spi==htonl(SPI_TRAP)
|
|
|| (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)))) {
|
|
int len;
|
|
|
|
ixs->ips.ips_ident_s.type = ixs->eroute->er_ident_s.type;
|
|
ixs->ips.ips_ident_s.id = ixs->eroute->er_ident_s.id;
|
|
ixs->ips.ips_ident_s.len = ixs->eroute->er_ident_s.len;
|
|
if (ixs->ips.ips_ident_s.len) {
|
|
len = ixs->ips.ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_SAlookup: "
|
|
"allocating %d bytes for ident_s shunt SA of HOLD: skb stored in HOLD.\n",
|
|
len);
|
|
if ((ixs->ips.ips_ident_s.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
|
|
printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
|
|
"Failed, tried to allocate %d bytes for source ident.\n",
|
|
len);
|
|
ixs->stats->tx_dropped++;
|
|
spin_unlock(&eroute_lock);
|
|
return IPSEC_XMIT_ERRMEMALLOC;
|
|
}
|
|
memcpy(ixs->ips.ips_ident_s.data, ixs->eroute->er_ident_s.data, len);
|
|
}
|
|
ixs->ips.ips_ident_d.type = ixs->eroute->er_ident_d.type;
|
|
ixs->ips.ips_ident_d.id = ixs->eroute->er_ident_d.id;
|
|
ixs->ips.ips_ident_d.len = ixs->eroute->er_ident_d.len;
|
|
if (ixs->ips.ips_ident_d.len) {
|
|
len = ixs->ips.ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident);
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_SAlookup: "
|
|
"allocating %d bytes for ident_d shunt SA of HOLD: skb stored in HOLD.\n",
|
|
len);
|
|
if ((ixs->ips.ips_ident_d.data = kmalloc(len, GFP_ATOMIC)) == NULL) {
|
|
printk(KERN_WARNING "klips_debug:ipsec_xmit_SAlookup: "
|
|
"Failed, tried to allocate %d bytes for dest ident.\n",
|
|
len);
|
|
ixs->stats->tx_dropped++;
|
|
spin_unlock(&eroute_lock);
|
|
return IPSEC_XMIT_ERRMEMALLOC;
|
|
}
|
|
memcpy(ixs->ips.ips_ident_d.data, ixs->eroute->er_ident_d.data, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
spin_unlock(&eroute_lock);
|
|
return IPSEC_XMIT_OK;
|
|
}
|
|
|
|
enum ipsec_xmit_value
|
|
ipsec_tunnel_restore_hard_header(struct ipsec_xmit_state*ixs)
|
|
{
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
|
|
"klips_debug:ipsec_xmit_restore_hard_header: "
|
|
"After recursive xforms -- head,tailroom: %d,%d\n",
|
|
skb_headroom(ixs->skb),
|
|
skb_tailroom(ixs->skb));
|
|
|
|
if(ixs->saved_header) {
|
|
if(skb_headroom(ixs->skb) < ixs->hard_header_len) {
|
|
printk(KERN_WARNING
|
|
"klips_error:ipsec_xmit_restore_hard_header: "
|
|
"tried to skb_push hhlen=%d, %d available. This should never happen, please report.\n",
|
|
ixs->hard_header_len,
|
|
skb_headroom(ixs->skb));
|
|
ixs->stats->tx_errors++;
|
|
return IPSEC_XMIT_PUSHPULLERR;
|
|
|
|
}
|
|
skb_push(ixs->skb, ixs->hard_header_len);
|
|
{
|
|
int i;
|
|
for (i = 0; i < ixs->hard_header_len; i++) {
|
|
ixs->skb->data[i] = ixs->saved_header[i];
|
|
}
|
|
}
|
|
}
|
|
#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
|
|
if (ixs->natt_type && ixs->natt_head) {
|
|
struct iphdr *ipp = ixs->skb->nh.iph;
|
|
struct udphdr *udp;
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_tunnel_start_xmit: "
|
|
"encapsulating packet into UDP (NAT-Traversal) (%d %d)\n",
|
|
ixs->natt_type, ixs->natt_head);
|
|
ixs->iphlen = ipp->ihl << 2;
|
|
ipp->tot_len =
|
|
htons(ntohs(ipp->tot_len) + ixs->natt_head);
|
|
if(skb_tailroom(ixs->skb) < ixs->natt_head) {
|
|
printk(KERN_WARNING "klips_error:ipsec_tunnel_start_xmit: "
|
|
"tried to skb_put %d, %d available. "
|
|
"This should never happen, please report.\n",
|
|
ixs->natt_head,
|
|
skb_tailroom(ixs->skb));
|
|
ixs->stats->tx_errors++;
|
|
return IPSEC_XMIT_ESPUDP;
|
|
}
|
|
skb_put(ixs->skb, ixs->natt_head);
|
|
udp = (struct udphdr *)((char *)ipp + ixs->iphlen);
|
|
/* move ESP hdr after UDP hdr */
|
|
memmove((void *)((char *)udp + ixs->natt_head),
|
|
(void *)(udp),
|
|
ntohs(ipp->tot_len) - ixs->iphlen - ixs->natt_head);
|
|
/* clear UDP & Non-IKE Markers (if any) */
|
|
memset(udp, 0, ixs->natt_head);
|
|
/* fill UDP with usefull informations ;-) */
|
|
udp->source = htons(ixs->natt_sport);
|
|
udp->dest = htons(ixs->natt_dport);
|
|
udp->len = htons(ntohs(ipp->tot_len) - ixs->iphlen);
|
|
/* set protocol */
|
|
ipp->protocol = IPPROTO_UDP;
|
|
/* fix IP checksum */
|
|
ipp->check = 0;
|
|
ipp->check = ip_fast_csum((unsigned char *)ipp, ipp->ihl);
|
|
}
|
|
#endif
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_CROUT,
|
|
"klips_debug:ipsec_xmit_restore_hard_header: "
|
|
"With hard_header, final head,tailroom: %d,%d\n",
|
|
skb_headroom(ixs->skb),
|
|
skb_tailroom(ixs->skb));
|
|
|
|
return IPSEC_XMIT_OK;
|
|
}
|
|
|
|
enum ipsec_xmit_value
|
|
ipsec_tunnel_send(struct ipsec_xmit_state*ixs)
|
|
{
|
|
#ifdef NETDEV_25
|
|
struct flowi fl;
|
|
#endif
|
|
|
|
#ifdef NET_21 /* 2.2 and 2.4 kernels */
|
|
/* new route/dst cache code from James Morris */
|
|
ixs->skb->dev = ixs->physdev;
|
|
#ifdef NETDEV_25
|
|
fl.oif = ixs->physdev->iflink;
|
|
fl.nl_u.ip4_u.daddr = ixs->skb->nh.iph->daddr;
|
|
fl.nl_u.ip4_u.saddr = ixs->pass ? 0 : ixs->skb->nh.iph->saddr;
|
|
fl.nl_u.ip4_u.tos = RT_TOS(ixs->skb->nh.iph->tos);
|
|
fl.proto = ixs->skb->nh.iph->protocol;
|
|
if ((ixs->error = ip_route_output_key(&ixs->route, &fl))) {
|
|
#else
|
|
/*skb_orphan(ixs->skb);*/
|
|
if((ixs->error = ip_route_output(&ixs->route,
|
|
ixs->skb->nh.iph->daddr,
|
|
ixs->pass ? 0 : ixs->skb->nh.iph->saddr,
|
|
RT_TOS(ixs->skb->nh.iph->tos),
|
|
/* mcr->rgb: should this be 0 instead? */
|
|
ixs->physdev->iflink))) {
|
|
#endif
|
|
ixs->stats->tx_errors++;
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_send: "
|
|
"ip_route_output failed with error code %d, rt->u.dst.dev=%s, dropped\n",
|
|
ixs->error,
|
|
ixs->route->u.dst.dev->name);
|
|
return IPSEC_XMIT_ROUTEERR;
|
|
}
|
|
if(ixs->dev == ixs->route->u.dst.dev) {
|
|
ip_rt_put(ixs->route);
|
|
/* This is recursion, drop it. */
|
|
ixs->stats->tx_errors++;
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_send: "
|
|
"suspect recursion, dev=rt->u.dst.dev=%s, dropped\n",
|
|
ixs->dev->name);
|
|
return IPSEC_XMIT_RECURSDETECT;
|
|
}
|
|
dst_release(ixs->skb->dst);
|
|
ixs->skb->dst = &ixs->route->u.dst;
|
|
ixs->stats->tx_bytes += ixs->skb->len;
|
|
if(ixs->skb->len < ixs->skb->nh.raw - ixs->skb->data) {
|
|
ixs->stats->tx_errors++;
|
|
printk(KERN_WARNING
|
|
"klips_error:ipsec_xmit_send: "
|
|
"tried to __skb_pull nh-data=%ld, %d available. This should never happen, please report.\n",
|
|
(unsigned long)(ixs->skb->nh.raw - ixs->skb->data),
|
|
ixs->skb->len);
|
|
return IPSEC_XMIT_PUSHPULLERR;
|
|
}
|
|
__skb_pull(ixs->skb, ixs->skb->nh.raw - ixs->skb->data);
|
|
#ifdef SKB_RESET_NFCT
|
|
if(!ixs->pass) {
|
|
nf_conntrack_put(ixs->skb->nfct);
|
|
ixs->skb->nfct = NULL;
|
|
}
|
|
#ifdef CONFIG_NETFILTER_DEBUG
|
|
ixs->skb->nf_debug = 0;
|
|
#endif /* CONFIG_NETFILTER_DEBUG */
|
|
#endif /* SKB_RESET_NFCT */
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_send: "
|
|
"...done, calling ip_send() on device:%s\n",
|
|
ixs->skb->dev ? ixs->skb->dev->name : "NULL");
|
|
KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->skb->nh.iph);
|
|
#ifdef NETDEV_23 /* 2.4 kernels */
|
|
{
|
|
int err;
|
|
|
|
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, ixs->skb, NULL, ixs->route->u.dst.dev,
|
|
ipsec_tunnel_xmit2);
|
|
if(err != NET_XMIT_SUCCESS && err != NET_XMIT_CN) {
|
|
if(net_ratelimit())
|
|
printk(KERN_ERR
|
|
"klips_error:ipsec_xmit_send: "
|
|
"ip_send() failed, err=%d\n",
|
|
-err);
|
|
ixs->stats->tx_errors++;
|
|
ixs->stats->tx_aborted_errors++;
|
|
ixs->skb = NULL;
|
|
return IPSEC_XMIT_IPSENDFAILURE;
|
|
}
|
|
}
|
|
#else /* NETDEV_23 */ /* 2.2 kernels */
|
|
ip_send(ixs->skb);
|
|
#endif /* NETDEV_23 */
|
|
#else /* NET_21 */ /* 2.0 kernels */
|
|
ixs->skb->arp = 1;
|
|
/* ISDN/ASYNC PPP from Matjaz Godec. */
|
|
/* skb->protocol = htons(ETH_P_IP); */
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_xmit_send: "
|
|
"...done, calling dev_queue_xmit() or ip_fragment().\n");
|
|
IP_SEND(ixs->skb, ixs->physdev);
|
|
#endif /* NET_21 */
|
|
ixs->stats->tx_packets++;
|
|
|
|
ixs->skb = NULL;
|
|
|
|
return IPSEC_XMIT_OK;
|
|
}
|
|
|
|
void
|
|
ipsec_tunnel_cleanup(struct ipsec_xmit_state*ixs)
|
|
{
|
|
#if defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE)
|
|
netif_wake_queue(ixs->dev);
|
|
#else /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
|
|
ixs->dev->tbusy = 0;
|
|
#endif /* defined(HAS_NETIF_QUEUE) || defined (HAVE_NETIF_QUEUE) */
|
|
if(ixs->saved_header) {
|
|
kfree(ixs->saved_header);
|
|
}
|
|
if(ixs->skb) {
|
|
dev_kfree_skb(ixs->skb, FREE_WRITE);
|
|
}
|
|
if(ixs->oskb) {
|
|
dev_kfree_skb(ixs->oskb, FREE_WRITE);
|
|
}
|
|
if (ixs->ips.ips_ident_s.data) {
|
|
kfree(ixs->ips.ips_ident_s.data);
|
|
}
|
|
if (ixs->ips.ips_ident_d.data) {
|
|
kfree(ixs->ips.ips_ident_d.data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function assumes it is being called from dev_queue_xmit()
|
|
* and that skb is filled properly by that function.
|
|
*/
|
|
int
|
|
ipsec_tunnel_start_xmit(struct sk_buff *skb, struct device *dev)
|
|
{
|
|
struct ipsec_xmit_state ixs_mem;
|
|
struct ipsec_xmit_state *ixs = &ixs_mem;
|
|
enum ipsec_xmit_value stat;
|
|
|
|
#ifdef CONFIG_IPSEC_NAT_TRAVERSAL
|
|
ixs->natt_type = 0, ixs->natt_head = 0;
|
|
ixs->natt_sport = 0, ixs->natt_dport = 0;
|
|
#endif
|
|
|
|
memset((caddr_t)ixs, 0, sizeof(*ixs));
|
|
ixs->oskb = NULL;
|
|
ixs->saved_header = NULL; /* saved copy of the hard header */
|
|
ixs->route = NULL;
|
|
memset((caddr_t)&(ixs->ips), 0, sizeof(ixs->ips));
|
|
ixs->dev = dev;
|
|
ixs->skb = skb;
|
|
|
|
stat = ipsec_xmit_sanity_check_dev(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
stat = ipsec_xmit_sanity_check_skb(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
stat = ipsec_tunnel_strip_hard_header(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
stat = ipsec_tunnel_SAlookup(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_tunnel_start_xmit: SAlookup failed: %d\n",
|
|
stat);
|
|
goto cleanup;
|
|
}
|
|
|
|
ixs->innersrc = ixs->iph->saddr;
|
|
/* start encapsulation loop here XXX */
|
|
do {
|
|
stat = ipsec_xmit_encap_bundle(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
if(stat == IPSEC_XMIT_PASS) {
|
|
goto bypass;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_XMIT,
|
|
"klips_debug:ipsec_tunnel_start_xmit: encap_bundle failed: %d\n",
|
|
stat);
|
|
goto cleanup;
|
|
}
|
|
|
|
ixs->matcher.sen_ip_src.s_addr = ixs->iph->saddr;
|
|
ixs->matcher.sen_ip_dst.s_addr = ixs->iph->daddr;
|
|
ixs->matcher.sen_proto = ixs->iph->protocol;
|
|
ipsec_extract_ports(ixs->iph, &ixs->matcher);
|
|
|
|
spin_lock(&eroute_lock);
|
|
ixs->eroute = ipsec_findroute(&ixs->matcher);
|
|
if(ixs->eroute) {
|
|
ixs->outgoing_said = ixs->eroute->er_said;
|
|
ixs->eroute_pid = ixs->eroute->er_pid;
|
|
ixs->eroute->er_count++;
|
|
ixs->eroute->er_lasttime = jiffies/HZ;
|
|
}
|
|
spin_unlock(&eroute_lock);
|
|
|
|
KLIPS_PRINT((debug_tunnel & DB_TN_XMIT) &&
|
|
/* ((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc)) */
|
|
(ixs->orgedst != ixs->outgoing_said.dst.s_addr) &&
|
|
ixs->outgoing_said.dst.s_addr &&
|
|
ixs->eroute,
|
|
"klips_debug:ipsec_tunnel_start_xmit: "
|
|
"We are recursing here.\n");
|
|
|
|
} while(/*((ixs->orgdst != ixs->newdst) || (ixs->orgsrc != ixs->newsrc))*/
|
|
(ixs->orgedst != ixs->outgoing_said.dst.s_addr) &&
|
|
ixs->outgoing_said.dst.s_addr &&
|
|
ixs->eroute);
|
|
|
|
stat = ipsec_tunnel_restore_hard_header(ixs);
|
|
if(stat != IPSEC_XMIT_OK) {
|
|
goto cleanup;
|
|
}
|
|
|
|
bypass:
|
|
stat = ipsec_tunnel_send(ixs);
|
|
|
|
cleanup:
|
|
ipsec_tunnel_cleanup(ixs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_NO_STATIC struct net_device_stats *
|
|
ipsec_tunnel_get_stats(struct device *dev)
|
|
{
|
|
return &(((struct ipsecpriv *)(dev->priv))->mystats);
|
|
}
|
|
|
|
/*
|
|
* Revectored calls.
|
|
* For each of these calls, a field exists in our private structure.
|
|
*/
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_hard_header(struct sk_buff *skb, struct device *dev,
|
|
unsigned short type, void *daddr, void *saddr, unsigned len)
|
|
{
|
|
struct ipsecpriv *prv = dev->priv;
|
|
struct device *tmp;
|
|
int ret;
|
|
struct net_device_stats *stats; /* This device's statistics */
|
|
|
|
if(skb == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"no skb...\n");
|
|
return -ENODATA;
|
|
}
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"no device...\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"skb->dev=%s dev=%s.\n",
|
|
skb->dev ? skb->dev->name : "NULL",
|
|
dev->name);
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"no private space associated with dev=%s\n",
|
|
dev->name ? dev->name : "NULL");
|
|
return -ENODEV;
|
|
}
|
|
|
|
stats = (struct net_device_stats *) &(prv->mystats);
|
|
|
|
if(prv->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"no physical device associated with dev=%s\n",
|
|
dev->name ? dev->name : "NULL");
|
|
stats->tx_dropped++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* check if we have to send a IPv6 packet. It might be a Router
|
|
Solicitation, where the building of the packet happens in
|
|
reverse order:
|
|
1. ll hdr,
|
|
2. IPv6 hdr,
|
|
3. ICMPv6 hdr
|
|
-> skb->nh.raw is still uninitialized when this function is
|
|
called!! If this is no IPv6 packet, we can print debugging
|
|
messages, otherwise we skip all debugging messages and just
|
|
build the ll header */
|
|
if(type != ETH_P_IPV6) {
|
|
/* execute this only, if we don't have to build the
|
|
header for a IPv6 packet */
|
|
if(!prv->hard_header) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"physical device has been detached, packet dropped 0p%p->0p%p len=%d type=%d dev=%s->NULL ",
|
|
saddr,
|
|
daddr,
|
|
len,
|
|
type,
|
|
dev->name);
|
|
#ifdef NET_21
|
|
KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->nh.iph->saddr),
|
|
(__u32)ntohl(skb->nh.iph->daddr) );
|
|
#else /* NET_21 */
|
|
KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->ip_hdr->saddr),
|
|
(__u32)ntohl(skb->ip_hdr->daddr) );
|
|
#endif /* NET_21 */
|
|
stats->tx_dropped++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
#define da ((struct device *)(prv->dev))->dev_addr
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"Revectored 0p%p->0p%p len=%d type=%d dev=%s->%s dev_addr=%02x:%02x:%02x:%02x:%02x:%02x ",
|
|
saddr,
|
|
daddr,
|
|
len,
|
|
type,
|
|
dev->name,
|
|
prv->dev->name,
|
|
da[0], da[1], da[2], da[3], da[4], da[5]);
|
|
#ifdef NET_21
|
|
KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->nh.iph->saddr),
|
|
(__u32)ntohl(skb->nh.iph->daddr) );
|
|
#else /* NET_21 */
|
|
KLIPS_PRINTMORE(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->ip_hdr->saddr),
|
|
(__u32)ntohl(skb->ip_hdr->daddr) );
|
|
#endif /* NET_21 */
|
|
} else {
|
|
KLIPS_PRINT(debug_tunnel,
|
|
"klips_debug:ipsec_tunnel_hard_header: "
|
|
"is IPv6 packet, skip debugging messages, only revector and build linklocal header.\n");
|
|
}
|
|
tmp = skb->dev;
|
|
skb->dev = prv->dev;
|
|
ret = prv->hard_header(skb, prv->dev, type, (void *)daddr, (void *)saddr, len);
|
|
skb->dev = tmp;
|
|
return ret;
|
|
}
|
|
|
|
DEBUG_NO_STATIC int
|
|
#ifdef NET_21
|
|
ipsec_tunnel_rebuild_header(struct sk_buff *skb)
|
|
#else /* NET_21 */
|
|
ipsec_tunnel_rebuild_header(void *buff, struct device *dev,
|
|
unsigned long raddr, struct sk_buff *skb)
|
|
#endif /* NET_21 */
|
|
{
|
|
struct ipsecpriv *prv = skb->dev->priv;
|
|
struct device *tmp;
|
|
int ret;
|
|
struct net_device_stats *stats; /* This device's statistics */
|
|
|
|
if(skb->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_rebuild_header: "
|
|
"no device...");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_rebuild_header: "
|
|
"no private space associated with dev=%s",
|
|
skb->dev->name ? skb->dev->name : "NULL");
|
|
return -ENODEV;
|
|
}
|
|
|
|
stats = (struct net_device_stats *) &(prv->mystats);
|
|
|
|
if(prv->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_rebuild_header: "
|
|
"no physical device associated with dev=%s",
|
|
skb->dev->name ? skb->dev->name : "NULL");
|
|
stats->tx_dropped++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(!prv->rebuild_header) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_rebuild_header: "
|
|
"physical device has been detached, packet dropped skb->dev=%s->NULL ",
|
|
skb->dev->name);
|
|
#ifdef NET_21
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->nh.iph->saddr),
|
|
(__u32)ntohl(skb->nh.iph->daddr) );
|
|
#else /* NET_21 */
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->ip_hdr->saddr),
|
|
(__u32)ntohl(skb->ip_hdr->daddr) );
|
|
#endif /* NET_21 */
|
|
stats->tx_dropped++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel: "
|
|
"Revectored rebuild_header dev=%s->%s ",
|
|
skb->dev->name, prv->dev->name);
|
|
#ifdef NET_21
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->nh.iph->saddr),
|
|
(__u32)ntohl(skb->nh.iph->daddr) );
|
|
#else /* NET_21 */
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"ip=%08x->%08x\n",
|
|
(__u32)ntohl(skb->ip_hdr->saddr),
|
|
(__u32)ntohl(skb->ip_hdr->daddr) );
|
|
#endif /* NET_21 */
|
|
tmp = skb->dev;
|
|
skb->dev = prv->dev;
|
|
|
|
#ifdef NET_21
|
|
ret = prv->rebuild_header(skb);
|
|
#else /* NET_21 */
|
|
ret = prv->rebuild_header(buff, prv->dev, raddr, skb);
|
|
#endif /* NET_21 */
|
|
skb->dev = tmp;
|
|
return ret;
|
|
}
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_set_mac_address(struct device *dev, void *addr)
|
|
{
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
struct net_device_stats *stats; /* This device's statistics */
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_set_mac_address: "
|
|
"no device...");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_set_mac_address: "
|
|
"no private space associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
return -ENODEV;
|
|
}
|
|
|
|
stats = (struct net_device_stats *) &(prv->mystats);
|
|
|
|
if(prv->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_set_mac_address: "
|
|
"no physical device associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
stats->tx_dropped++;
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(!prv->set_mac_address) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_set_mac_address: "
|
|
"physical device has been detached, cannot set - skb->dev=%s->NULL\n",
|
|
dev->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_set_mac_address: "
|
|
"Revectored dev=%s->%s addr=0p%p\n",
|
|
dev->name, prv->dev->name, addr);
|
|
return prv->set_mac_address(prv->dev, addr);
|
|
|
|
}
|
|
|
|
#ifndef NET_21
|
|
DEBUG_NO_STATIC void
|
|
ipsec_tunnel_cache_bind(struct hh_cache **hhp, struct device *dev,
|
|
unsigned short htype, __u32 daddr)
|
|
{
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
struct net_device_stats *stats; /* This device's statistics */
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_bind: "
|
|
"no device...");
|
|
return;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_bind: "
|
|
"no private space associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
return;
|
|
}
|
|
|
|
stats = (struct net_device_stats *) &(prv->mystats);
|
|
|
|
if(prv->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_bind: "
|
|
"no physical device associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
stats->tx_dropped++;
|
|
return;
|
|
}
|
|
|
|
if(!prv->header_cache_bind) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_bind: "
|
|
"physical device has been detached, cannot set - skb->dev=%s->NULL\n",
|
|
dev->name);
|
|
stats->tx_dropped++;
|
|
return;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_bind: "
|
|
"Revectored \n");
|
|
prv->header_cache_bind(hhp, prv->dev, htype, daddr);
|
|
return;
|
|
}
|
|
#endif /* !NET_21 */
|
|
|
|
|
|
DEBUG_NO_STATIC void
|
|
ipsec_tunnel_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr)
|
|
{
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
struct net_device_stats *stats; /* This device's statistics */
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_update: "
|
|
"no device...");
|
|
return;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_update: "
|
|
"no private space associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
return;
|
|
}
|
|
|
|
stats = (struct net_device_stats *) &(prv->mystats);
|
|
|
|
if(prv->dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_update: "
|
|
"no physical device associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
stats->tx_dropped++;
|
|
return;
|
|
}
|
|
|
|
if(!prv->header_cache_update) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_cache_update: "
|
|
"physical device has been detached, cannot set - skb->dev=%s->NULL\n",
|
|
dev->name);
|
|
return;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel: "
|
|
"Revectored cache_update\n");
|
|
prv->header_cache_update(hh, prv->dev, haddr);
|
|
return;
|
|
}
|
|
|
|
#ifdef NET_21
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_neigh_setup(struct neighbour *n)
|
|
{
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_neigh_setup:\n");
|
|
|
|
if (n->nud_state == NUD_NONE) {
|
|
n->ops = &arp_broken_ops;
|
|
n->output = n->ops->output;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_neigh_setup_dev(struct device *dev, struct neigh_parms *p)
|
|
{
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_neigh_setup_dev: "
|
|
"setting up %s\n",
|
|
dev ? dev->name : "NULL");
|
|
|
|
if (p->tbl->family == AF_INET) {
|
|
p->neigh_setup = ipsec_tunnel_neigh_setup;
|
|
p->ucast_probes = 0;
|
|
p->mcast_probes = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* NET_21 */
|
|
|
|
/*
|
|
* We call the attach routine to attach another device.
|
|
*/
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_attach(struct device *dev, struct device *physdev)
|
|
{
|
|
int i;
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_attach: "
|
|
"no device...");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_attach: "
|
|
"no private space associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
return -ENODATA;
|
|
}
|
|
|
|
prv->dev = physdev;
|
|
prv->hard_start_xmit = physdev->hard_start_xmit;
|
|
prv->get_stats = physdev->get_stats;
|
|
|
|
if (physdev->hard_header) {
|
|
prv->hard_header = physdev->hard_header;
|
|
dev->hard_header = ipsec_tunnel_hard_header;
|
|
} else
|
|
dev->hard_header = NULL;
|
|
|
|
if (physdev->rebuild_header) {
|
|
prv->rebuild_header = physdev->rebuild_header;
|
|
dev->rebuild_header = ipsec_tunnel_rebuild_header;
|
|
} else
|
|
dev->rebuild_header = NULL;
|
|
|
|
if (physdev->set_mac_address) {
|
|
prv->set_mac_address = physdev->set_mac_address;
|
|
dev->set_mac_address = ipsec_tunnel_set_mac_address;
|
|
} else
|
|
dev->set_mac_address = NULL;
|
|
|
|
#ifndef NET_21
|
|
if (physdev->header_cache_bind) {
|
|
prv->header_cache_bind = physdev->header_cache_bind;
|
|
dev->header_cache_bind = ipsec_tunnel_cache_bind;
|
|
} else
|
|
dev->header_cache_bind = NULL;
|
|
#endif /* !NET_21 */
|
|
|
|
if (physdev->header_cache_update) {
|
|
prv->header_cache_update = physdev->header_cache_update;
|
|
dev->header_cache_update = ipsec_tunnel_cache_update;
|
|
} else
|
|
dev->header_cache_update = NULL;
|
|
|
|
dev->hard_header_len = physdev->hard_header_len;
|
|
|
|
#ifdef NET_21
|
|
/* prv->neigh_setup = physdev->neigh_setup; */
|
|
dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
|
|
#endif /* NET_21 */
|
|
dev->mtu = 16260; /* 0xfff0; */ /* dev->mtu; */
|
|
prv->mtu = physdev->mtu;
|
|
|
|
#ifdef PHYSDEV_TYPE
|
|
dev->type = physdev->type; /* ARPHRD_TUNNEL; */
|
|
#endif /* PHYSDEV_TYPE */
|
|
|
|
dev->addr_len = physdev->addr_len;
|
|
for (i=0; i<dev->addr_len; i++) {
|
|
dev->dev_addr[i] = physdev->dev_addr[i];
|
|
}
|
|
#ifdef CONFIG_IPSEC_DEBUG
|
|
if(debug_tunnel & DB_TN_INIT) {
|
|
printk(KERN_INFO "klips_debug:ipsec_tunnel_attach: "
|
|
"physical device %s being attached has HW address: %2x",
|
|
physdev->name, physdev->dev_addr[0]);
|
|
for (i=1; i < physdev->addr_len; i++) {
|
|
printk(":%02x", physdev->dev_addr[i]);
|
|
}
|
|
printk("\n");
|
|
}
|
|
#endif /* CONFIG_IPSEC_DEBUG */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We call the detach routine to detach the ipsec tunnel from another device.
|
|
*/
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_detach(struct device *dev)
|
|
{
|
|
int i;
|
|
struct ipsecpriv *prv = dev->priv;
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_detach: "
|
|
"no device...");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if(prv == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_REVEC,
|
|
"klips_debug:ipsec_tunnel_detach: "
|
|
"no private space associated with dev=%s",
|
|
dev->name ? dev->name : "NULL");
|
|
return -ENODATA;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_detach: "
|
|
"physical device %s being detached from virtual device %s\n",
|
|
prv->dev ? prv->dev->name : "NULL",
|
|
dev->name);
|
|
|
|
ipsec_dev_put(prv->dev);
|
|
prv->dev = NULL;
|
|
prv->hard_start_xmit = NULL;
|
|
prv->get_stats = NULL;
|
|
|
|
prv->hard_header = NULL;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->hard_header = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
|
|
prv->rebuild_header = NULL;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->rebuild_header = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
|
|
prv->set_mac_address = NULL;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->set_mac_address = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
|
|
#ifndef NET_21
|
|
prv->header_cache_bind = NULL;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->header_cache_bind = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
#endif /* !NET_21 */
|
|
|
|
prv->header_cache_update = NULL;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->header_cache_update = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
|
|
#ifdef NET_21
|
|
/* prv->neigh_setup = NULL; */
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->neigh_setup = NULL;
|
|
#endif /* DETACH_AND_DOWN */
|
|
#endif /* NET_21 */
|
|
dev->hard_header_len = 0;
|
|
#ifdef DETACH_AND_DOWN
|
|
dev->mtu = 0;
|
|
#endif /* DETACH_AND_DOWN */
|
|
prv->mtu = 0;
|
|
for (i=0; i<MAX_ADDR_LEN; i++) {
|
|
dev->dev_addr[i] = 0;
|
|
}
|
|
dev->addr_len = 0;
|
|
#ifdef PHYSDEV_TYPE
|
|
dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */
|
|
#endif /* PHYSDEV_TYPE */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* We call the clear routine to detach all ipsec tunnels from other devices.
|
|
*/
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_clear(void)
|
|
{
|
|
int i;
|
|
struct device *ipsecdev = NULL, *prvdev;
|
|
struct ipsecpriv *prv;
|
|
char name[9];
|
|
int ret;
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_clear: .\n");
|
|
|
|
for(i = 0; i < IPSEC_NUM_IF; i++) {
|
|
ipsecdev = ipsecdevices[i];
|
|
if(ipsecdev != NULL) {
|
|
if((prv = (struct ipsecpriv *)(ipsecdev->priv))) {
|
|
prvdev = (struct device *)(prv->dev);
|
|
if(prvdev) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_clear: "
|
|
"physical device for device %s is %s\n",
|
|
name, prvdev->name);
|
|
if((ret = ipsec_tunnel_detach(ipsecdev))) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_clear: "
|
|
"error %d detatching device %s from device %s.\n",
|
|
ret, name, prvdev->name);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_NO_STATIC int
|
|
ipsec_tunnel_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
|
|
{
|
|
struct ipsectunnelconf *cf = (struct ipsectunnelconf *)&ifr->ifr_data;
|
|
struct ipsecpriv *prv = dev->priv;
|
|
struct device *them; /* physical device */
|
|
#ifdef CONFIG_IP_ALIAS
|
|
char *colon;
|
|
char realphysname[IFNAMSIZ];
|
|
#endif /* CONFIG_IP_ALIAS */
|
|
|
|
if(dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"device not supplied.\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"tncfg service call #%d for dev=%s\n",
|
|
cmd,
|
|
dev->name ? dev->name : "NULL");
|
|
switch (cmd) {
|
|
/* attach a virtual ipsec? device to a physical device */
|
|
case IPSEC_SET_DEV:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"calling ipsec_tunnel_attatch...\n");
|
|
#ifdef CONFIG_IP_ALIAS
|
|
/* If this is an IP alias interface, get its real physical name */
|
|
strncpy(realphysname, cf->cf_name, IFNAMSIZ);
|
|
realphysname[IFNAMSIZ-1] = 0;
|
|
colon = strchr(realphysname, ':');
|
|
if (colon) *colon = 0;
|
|
them = ipsec_dev_get(realphysname);
|
|
#else /* CONFIG_IP_ALIAS */
|
|
them = ipsec_dev_get(cf->cf_name);
|
|
#endif /* CONFIG_IP_ALIAS */
|
|
|
|
if (them == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"physical device %s requested is null\n",
|
|
cf->cf_name);
|
|
return -ENXIO;
|
|
}
|
|
|
|
#if 0
|
|
if (them->flags & IFF_UP) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"physical device %s requested is not up.\n",
|
|
cf->cf_name);
|
|
ipsec_dev_put(them);
|
|
return -ENXIO;
|
|
}
|
|
#endif
|
|
|
|
if (prv && prv->dev) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"virtual device is already connected to %s.\n",
|
|
prv->dev->name ? prv->dev->name : "NULL");
|
|
ipsec_dev_put(them);
|
|
return -EBUSY;
|
|
}
|
|
return ipsec_tunnel_attach(dev, them);
|
|
|
|
case IPSEC_DEL_DEV:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"calling ipsec_tunnel_detatch.\n");
|
|
if (! prv->dev) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"physical device not connected.\n");
|
|
return -ENODEV;
|
|
}
|
|
return ipsec_tunnel_detach(dev);
|
|
|
|
case IPSEC_CLR_DEV:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"calling ipsec_tunnel_clear.\n");
|
|
return ipsec_tunnel_clear();
|
|
|
|
default:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_ioctl: "
|
|
"unknown command %d.\n",
|
|
cmd);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
int
|
|
ipsec_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
|
|
{
|
|
struct device *dev = ptr;
|
|
struct device *ipsec_dev;
|
|
struct ipsecpriv *priv;
|
|
int i;
|
|
|
|
if (dev == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"dev=NULL for event type %ld.\n",
|
|
event);
|
|
return(NOTIFY_DONE);
|
|
}
|
|
|
|
/* check for loopback devices */
|
|
if (dev && (dev->flags & IFF_LOOPBACK)) {
|
|
return(NOTIFY_DONE);
|
|
}
|
|
|
|
switch (event) {
|
|
case NETDEV_DOWN:
|
|
/* look very carefully at the scope of these compiler
|
|
directives before changing anything... -- RGB */
|
|
#ifdef NET_21
|
|
case NETDEV_UNREGISTER:
|
|
switch (event) {
|
|
case NETDEV_DOWN:
|
|
#endif /* NET_21 */
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_DOWN dev=%s flags=%x\n",
|
|
dev->name,
|
|
dev->flags);
|
|
if(strncmp(dev->name, "ipsec", strlen("ipsec")) == 0) {
|
|
printk(KERN_CRIT "IPSEC EVENT: KLIPS device %s shut down.\n",
|
|
dev->name);
|
|
}
|
|
#ifdef NET_21
|
|
break;
|
|
case NETDEV_UNREGISTER:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_UNREGISTER dev=%s flags=%x\n",
|
|
dev->name,
|
|
dev->flags);
|
|
break;
|
|
}
|
|
#endif /* NET_21 */
|
|
|
|
/* find the attached physical device and detach it. */
|
|
for(i = 0; i < IPSEC_NUM_IF; i++) {
|
|
ipsec_dev = ipsecdevices[i];
|
|
|
|
if(ipsec_dev) {
|
|
priv = (struct ipsecpriv *)(ipsec_dev->priv);
|
|
if(priv) {
|
|
;
|
|
if(((struct device *)(priv->dev)) == dev) {
|
|
/* dev_close(ipsec_dev); */
|
|
/* return */ ipsec_tunnel_detach(ipsec_dev);
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"device '%s' has been detached.\n",
|
|
ipsec_dev->name);
|
|
break;
|
|
}
|
|
} else {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"device '%s' has no private data space!\n",
|
|
ipsec_dev->name);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NETDEV_UP:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_UP dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
#ifdef NET_21
|
|
case NETDEV_REBOOT:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_REBOOT dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
case NETDEV_CHANGE:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_CHANGE dev=%s flags=%x\n",
|
|
dev->name,
|
|
dev->flags);
|
|
break;
|
|
case NETDEV_REGISTER:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_REGISTER dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
case NETDEV_CHANGEMTU:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_CHANGEMTU dev=%s to mtu=%d\n",
|
|
dev->name,
|
|
dev->mtu);
|
|
break;
|
|
case NETDEV_CHANGEADDR:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_CHANGEADDR dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
case NETDEV_GOING_DOWN:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_GOING_DOWN dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
case NETDEV_CHANGENAME:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"NETDEV_CHANGENAME dev=%s\n",
|
|
dev->name);
|
|
break;
|
|
#endif /* NET_21 */
|
|
default:
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_device_event: "
|
|
"event type %ld unrecognised for dev=%s\n",
|
|
event,
|
|
dev->name);
|
|
break;
|
|
}
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
/*
|
|
* Called when an ipsec tunnel device is initialized.
|
|
* The ipsec tunnel device structure is passed to us.
|
|
*/
|
|
|
|
int
|
|
ipsec_tunnel_init(struct device *dev)
|
|
{
|
|
int i;
|
|
|
|
KLIPS_PRINT(debug_tunnel,
|
|
"klips_debug:ipsec_tunnel_init: "
|
|
"allocating %lu bytes initialising device: %s\n",
|
|
(unsigned long) sizeof(struct ipsecpriv),
|
|
dev->name ? dev->name : "NULL");
|
|
|
|
/* Add our tunnel functions to the device */
|
|
dev->open = ipsec_tunnel_open;
|
|
dev->stop = ipsec_tunnel_close;
|
|
dev->hard_start_xmit = ipsec_tunnel_start_xmit;
|
|
dev->get_stats = ipsec_tunnel_get_stats;
|
|
|
|
dev->priv = kmalloc(sizeof(struct ipsecpriv), GFP_KERNEL);
|
|
if (dev->priv == NULL)
|
|
return -ENOMEM;
|
|
memset((caddr_t)(dev->priv), 0, sizeof(struct ipsecpriv));
|
|
|
|
for(i = 0; i < sizeof(zeroes); i++) {
|
|
((__u8*)(zeroes))[i] = 0;
|
|
}
|
|
|
|
#ifndef NET_21
|
|
/* Initialize the tunnel device structure */
|
|
for (i = 0; i < DEV_NUMBUFFS; i++)
|
|
skb_queue_head_init(&dev->buffs[i]);
|
|
#endif /* !NET_21 */
|
|
|
|
dev->set_multicast_list = NULL;
|
|
dev->do_ioctl = ipsec_tunnel_ioctl;
|
|
dev->hard_header = NULL;
|
|
dev->rebuild_header = NULL;
|
|
dev->set_mac_address = NULL;
|
|
#ifndef NET_21
|
|
dev->header_cache_bind = NULL;
|
|
#endif /* !NET_21 */
|
|
dev->header_cache_update= NULL;
|
|
|
|
#ifdef NET_21
|
|
/* prv->neigh_setup = NULL; */
|
|
dev->neigh_setup = ipsec_tunnel_neigh_setup_dev;
|
|
#endif /* NET_21 */
|
|
dev->hard_header_len = 0;
|
|
dev->mtu = 0;
|
|
dev->addr_len = 0;
|
|
dev->type = ARPHRD_VOID; /* ARPHRD_TUNNEL; */ /* ARPHRD_ETHER; */
|
|
dev->tx_queue_len = 10; /* Small queue */
|
|
memset((caddr_t)(dev->broadcast),0xFF, ETH_ALEN); /* what if this is not attached to ethernet? */
|
|
|
|
/* New-style flags. */
|
|
dev->flags = IFF_NOARP /* 0 */ /* Petr Novak */;
|
|
#ifdef NET_21
|
|
dev_init_buffers(dev);
|
|
#else /* NET_21 */
|
|
dev->family = AF_INET;
|
|
dev->pa_addr = 0;
|
|
dev->pa_brdaddr = 0;
|
|
dev->pa_mask = 0;
|
|
dev->pa_alen = 4;
|
|
#endif /* NET_21 */
|
|
|
|
/* We're done. Have I forgotten anything? */
|
|
return 0;
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
/* Module specific interface (but it links with the rest of IPSEC) */
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
int
|
|
ipsec_tunnel_probe(struct device *dev)
|
|
{
|
|
ipsec_tunnel_init(dev);
|
|
return 0;
|
|
}
|
|
|
|
struct device *ipsecdevices[IPSEC_NUM_IF];
|
|
|
|
int
|
|
ipsec_tunnel_init_devices(void)
|
|
{
|
|
int i;
|
|
char name[IFNAMSIZ];
|
|
struct device *dev_ipsec;
|
|
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"creating and registering IPSEC_NUM_IF=%u devices, allocating %lu per device, IFNAMSIZ=%u.\n",
|
|
IPSEC_NUM_IF,
|
|
(unsigned long) (sizeof(struct device) + IFNAMSIZ),
|
|
IFNAMSIZ);
|
|
|
|
for(i = 0; i < IPSEC_NUM_IF; i++) {
|
|
sprintf(name, IPSEC_DEV_FORMAT, i);
|
|
dev_ipsec = (struct device*)kmalloc(sizeof(struct device), GFP_KERNEL);
|
|
if (dev_ipsec == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"failed to allocate memory for device %s, quitting device init.\n",
|
|
name);
|
|
return -ENOMEM;
|
|
}
|
|
memset((caddr_t)dev_ipsec, 0, sizeof(struct device));
|
|
#ifdef NETDEV_23
|
|
strncpy(dev_ipsec->name, name, sizeof(dev_ipsec->name));
|
|
#else /* NETDEV_23 */
|
|
dev_ipsec->name = (char*)kmalloc(IFNAMSIZ, GFP_KERNEL);
|
|
if (dev_ipsec->name == NULL) {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"failed to allocate memory for device %s name, quitting device init.\n",
|
|
name);
|
|
return -ENOMEM;
|
|
}
|
|
memset((caddr_t)dev_ipsec->name, 0, IFNAMSIZ);
|
|
strncpy(dev_ipsec->name, name, IFNAMSIZ);
|
|
#endif /* NETDEV_23 */
|
|
dev_ipsec->next = NULL;
|
|
dev_ipsec->init = &ipsec_tunnel_probe;
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"registering device %s\n",
|
|
dev_ipsec->name);
|
|
|
|
/* reference and hold the device reference */
|
|
dev_hold(dev_ipsec);
|
|
ipsecdevices[i]=dev_ipsec;
|
|
|
|
if (register_netdev(dev_ipsec) != 0) {
|
|
KLIPS_PRINT(1 || debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"registering device %s failed, quitting device init.\n",
|
|
dev_ipsec->name);
|
|
return -EIO;
|
|
} else {
|
|
KLIPS_PRINT(debug_tunnel & DB_TN_INIT,
|
|
"klips_debug:ipsec_tunnel_init_devices: "
|
|
"registering device %s succeeded, continuing...\n",
|
|
dev_ipsec->name);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* void */
|
|
int
|
|
ipsec_tunnel_cleanup_devices(void)
|
|
{
|
|
int error = 0;
|
|
int i;
|
|
char name[32];
|
|
struct device *dev_ipsec;
|
|
|
|
for(i = 0; i < IPSEC_NUM_IF; i++) {
|
|
dev_ipsec = ipsecdevices[i];
|
|
if(dev_ipsec == NULL) {
|
|
continue;
|
|
}
|
|
|
|
/* release reference */
|
|
ipsecdevices[i]=NULL;
|
|
ipsec_dev_put(dev_ipsec);
|
|
|
|
KLIPS_PRINT(debug_tunnel, "Unregistering %s (refcnt=%d)\n",
|
|
name,
|
|
atomic_read(&dev_ipsec->refcnt));
|
|
unregister_netdev(dev_ipsec);
|
|
KLIPS_PRINT(debug_tunnel, "Unregisted %s\n", name);
|
|
#ifndef NETDEV_23
|
|
kfree(dev_ipsec->name);
|
|
dev_ipsec->name=NULL;
|
|
#endif /* !NETDEV_23 */
|
|
kfree(dev_ipsec->priv);
|
|
dev_ipsec->priv=NULL;
|
|
}
|
|
return error;
|
|
}
|