3263 lines
104 KiB
C
3263 lines
104 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_tunnel.c,v $ $Revision: 1.147.10.4 $
|
|
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_tunnel.c,v $
|
|
* $Author: hxie $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* Implements the following tunnel type:
|
|
* - IPIP tunnel (RFC 1853)
|
|
* - Minimal Encapsulation for IPv4 (RFC 2004)
|
|
* - IPv6 tunnels (RFC 2473)
|
|
* - Transmission of IPv6 over IPv4 Domains without Explicit Tunnels (6over4) (RFC 2529)
|
|
* - Generic Packet Encapsulation (RFC 2784)
|
|
* - 6to4 tunnels (RFC 3056)
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "ipnet_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_USE_TUNNEL
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_cstyle.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_type.h>
|
|
|
|
#include "ipnet_h.h"
|
|
#include "ipnet_loopback.h"
|
|
#include "ipnet_netif.h"
|
|
#include "ipnet_tunnel.h"
|
|
|
|
#ifdef IPCOM_WV_INSTRUMENTATION
|
|
#include <ipcom_windview.h>
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/* NAT-T translation */
|
|
typedef struct Ipnet_tunnel_nat_t
|
|
{
|
|
int type;
|
|
Ip_u8 *proto;
|
|
Ip_u16 **cksum;
|
|
Ip_u16 sport;
|
|
Ip_u16 dport;
|
|
}
|
|
Ipnet_tunnel_nat_t;
|
|
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
/*
|
|
* Search key for locating the correct tunnel interface for something
|
|
* tunneled over IPv4
|
|
*/
|
|
struct Ipnet_tunnel_ip4_key {
|
|
Ipnet_pkt_ip *outer_ip_hdr; /* Outer IPv4 header */
|
|
Ip_u16 vr; /* Virtual Router Index */
|
|
};
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
/*
|
|
* Search key for locating the correct tunnel interface for something
|
|
* tunneled over IPv6
|
|
*/
|
|
struct Ipnet_tunnel_ip6_key {
|
|
Ipnet_pkt_ip6 *outer_ip_hdr; /* Outer IPv6 header */
|
|
Ip_u8 ip_proto; /* Value of the 'nxt' field of the
|
|
* header that precedes the inner IP
|
|
* header */
|
|
Ip_u16 vr; /* Virtual Router Index */
|
|
};
|
|
#endif
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
static union Ipnet_neigh_key_union ipnet_tunnel_neigh_addr = { { 0, }, };
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_neigh_free
|
|
*===========================================================================
|
|
* Description: Frees the memory held by the tunnel neighbor information.
|
|
* Parameters: neigh - a tunnel neighbor entry.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_neigh_free(Ipnet_neigh *neigh)
|
|
{
|
|
ipcom_free(neigh->neigh_data);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_neigh_create
|
|
*===========================================================================
|
|
* Description: Creates a tunnel neighbor.
|
|
* Parameters: domain - IP_AF_INET or IP_AF_INET6
|
|
* netif - pointer to a tunnel interface.
|
|
* flow_spec - flow specification that needs this neighbor
|
|
* rt - route entry matching the flow.
|
|
* Returns: The neighbor entry.
|
|
*
|
|
*/
|
|
IP_STATIC Ipnet_neigh *
|
|
ipnet_tunnel_neigh_create(int domain,
|
|
Ipnet_netif *netif,
|
|
const Ipnet_flow_spec *flow_spec,
|
|
Ipnet_route_entry *rt)
|
|
{
|
|
Ipnet_neigh *neigh = IP_NULL;
|
|
|
|
IPCOM_UNUSED_ARG(flow_spec);
|
|
|
|
if (IP_BIT_ISFALSE(IPNET_RTF_TUNNELEND, rt->hdr.flags))
|
|
{
|
|
/*
|
|
* Destination node is link local to this node. Go 'LINK' neigh
|
|
* here, since it should be invisible to any and all
|
|
* netlink/route commands.
|
|
*/
|
|
neigh = ipnet_neigh_get(IPNET_NEIGH_AF_LINK_SET(domain),
|
|
&ipnet_tunnel_neigh_addr,
|
|
netif,
|
|
IPNET_NEIGH_CAN_CREATE_NO_CHECK);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Destination node is reachable via a gateway
|
|
*/
|
|
const void *neigh_addr = IP_NULL;
|
|
union Ip_sockaddr_union *su;
|
|
|
|
su = (union Ip_sockaddr_union *) rt->gateway;
|
|
|
|
switch (domain)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
neigh_addr = &su->sin.sin_addr;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
neigh_addr = &su->sin6.sin6_addr;
|
|
break;
|
|
#endif
|
|
default:
|
|
return IP_NULL;
|
|
}
|
|
|
|
neigh = ipnet_neigh_get(domain,
|
|
neigh_addr,
|
|
netif,
|
|
IPNET_NEIGH_CAN_CREATE_NO_CHECK);
|
|
|
|
if (IP_LIKELY(IP_NULL != neigh))
|
|
{
|
|
neigh->neigh_data = ipcom_malloc(IPCOM_SA_LEN_GET(su));
|
|
if (IP_UNLIKELY(neigh->neigh_data == IP_NULL))
|
|
{
|
|
ipnet_neigh_set_state(neigh, IPNET_ND_DEAD);
|
|
ipnet_neigh_release(neigh);
|
|
neigh = IP_NULL;
|
|
}
|
|
else
|
|
{
|
|
ipcom_memcpy(neigh->neigh_data, su, IPCOM_SA_LEN_GET(su));
|
|
neigh->neigh_data_free = ipnet_tunnel_neigh_free;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return neigh;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_rfc3519_header
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_nat_t(Ipcom_pkt *pkt,
|
|
Ipnet_tunnel_nat_t *nat_param)
|
|
{
|
|
Ip_u8 *nat_t;
|
|
Ip_u16 *udp_csum;
|
|
Ipnet_pkt_udp *udp_hdr;
|
|
int ret;
|
|
|
|
/* Kind of translation */
|
|
switch (nat_param->type)
|
|
{
|
|
case IP_UDP_NAT_T_RFC3519:
|
|
/* 8 for UDP; 4 for RFC3519 header */
|
|
if (pkt->start < (4 + IPNET_UDP_HDR_SIZE))
|
|
{
|
|
ret = ipnet_increase_hdr_space(&pkt, IP_FALSE);
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
|
|
1, 1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_SEND,
|
|
ipnet_tunnel_nat_t, IPCOM_WV_NETD_MSGSIZE,
|
|
IPCOM_WV_IPNET_TUNNEL_MODULE, IPCOM_WV_NETD_IP4);
|
|
IPNET_STATS(ip4_output_hdr_space++);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* First; create the RFC3519 header */
|
|
nat_t = ipcom_pkt_push_front(pkt, 4);
|
|
|
|
/* UDP HEADER
|
|
* 4 byte NAT-T header;
|
|
* TYPE(1 byte) := 4
|
|
* NH(1 byte) := 4, 47 or 55
|
|
* RESERVED(2 bytes) := 0
|
|
*/
|
|
nat_t[0] = 4;
|
|
nat_t[1] = *nat_param->proto;
|
|
nat_t[2] = nat_t[3] = 0;
|
|
goto udp_encap;
|
|
|
|
case IP_UDP_NAT_T_DSMIPV6_TLV:
|
|
{
|
|
Ip_u16 len = 0;
|
|
|
|
/* 8 for UDP; 4 for TLV header */
|
|
if (pkt->start < 4)
|
|
{
|
|
ret = ipnet_increase_hdr_space(&pkt, IP_FALSE);
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
|
|
1, 1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_SEND,
|
|
ipnet_tunnel_nat_t, IPCOM_WV_NETD_MSGSIZE,
|
|
IPCOM_WV_IPNET_TUNNEL_MODULE, IPCOM_WV_NETD_IP4);
|
|
IPNET_STATS(ip4_output_hdr_space++);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* UDP HEADER
|
|
* 4 byte TLV header;
|
|
* TYPE(1 byte) := 0
|
|
* NH(1 byte) := Protocol Number
|
|
* LEN(2 bytes) := Length
|
|
*/
|
|
len = ipcom_pkt_get_length(pkt);
|
|
nat_t = ipcom_pkt_push_front(pkt, 4);
|
|
nat_t[0] = 0;
|
|
nat_t[1] = *nat_param->proto;
|
|
IP_SET_HTONS(&nat_t[2], len);
|
|
}
|
|
/* FALL THROUGH */
|
|
case IP_UDP_NAT_T_DSMIPV6:
|
|
udp_encap:
|
|
/* 8 for UDP */
|
|
if (pkt->start < (IPNET_UDP_HDR_SIZE))
|
|
{
|
|
ret = ipnet_increase_hdr_space(&pkt, IP_FALSE);
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
|
|
1, 1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_SEND,
|
|
ipnet_tunnel_nat_t, IPCOM_WV_NETD_MSGSIZE,
|
|
IPCOM_WV_IPNET_TUNNEL_MODULE, IPCOM_WV_NETD_IP4);
|
|
IPNET_STATS(ip4_output_hdr_space++);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* Setup the UDP header */
|
|
udp_hdr = ipcom_pkt_push_front(pkt, IPNET_UDP_HDR_SIZE);
|
|
udp_hdr->sport = nat_param->sport;
|
|
udp_hdr->dport = nat_param->dport;
|
|
udp_hdr->sum = 0;
|
|
udp_hdr->ulen = (Ip_u16) ip_htons(ipcom_pkt_get_length(pkt));
|
|
|
|
/* Calculate the UDP body checksum */
|
|
pkt->chk = ipcom_in_checksum_update_pkt(pkt, 0);
|
|
|
|
/* Store the checksum field */
|
|
udp_csum = &udp_hdr->sum;
|
|
ipcom_pkt_set_info(pkt,
|
|
IPNET_PKT_INFO_L4_CHKSUM_PTR,
|
|
sizeof(udp_csum),
|
|
&udp_csum);
|
|
|
|
#if 0
|
|
*nat_param->cksum = &udp_hdr->sum;
|
|
#endif
|
|
|
|
/* Set the protocol to UDP */
|
|
*nat_param->proto = IP_IPPROTO_UDP;
|
|
|
|
/* Done */
|
|
return 0;
|
|
}
|
|
|
|
/**/
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_min_encap_create_header
|
|
*===========================================================================
|
|
* Description: Creates a minimal encapsulation header and removes the
|
|
* original inner IPv4 header.
|
|
* Parameters: pkt - The packet where the minimal encapsulation header should
|
|
* be written to.
|
|
* outer_ipv4_from_addr - The outer IPv4 address or IP_NULL if
|
|
* that address is unknown.
|
|
* ttl - Pointer to where the TTL value should be stored.
|
|
* Returns: 0 = success, <0 = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_min_encap_create_header(Ipcom_pkt *pkt,
|
|
IP_CONST struct Ip_in_addr *outer_ipv4_from_addr,
|
|
Ip_u8 *ttl)
|
|
{
|
|
Ipnet_pkt_ip *inner_ipv4_hdr;
|
|
int inner_ipv4_len;
|
|
Ip_u8 inner_ipv4_proto;
|
|
Ipnet_pkt_min_encap *min_encap_hdr;
|
|
int min_encap_len;
|
|
|
|
inner_ipv4_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
|
|
if ((inner_ipv4_hdr->v_hl & 0xf0) != 0x40)
|
|
{
|
|
IPCOM_LOG0(WARNING, "Tunnel: IP_IPPROTO_MOBILE is only supported if the inner header is IPv4");
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
if (IPNET_ISFRAG(inner_ipv4_hdr->off))
|
|
{
|
|
/* Do ordinary IPIP tunneling; we can't deal with fragments */
|
|
return 1;
|
|
}
|
|
|
|
if (*ttl == 0)
|
|
*ttl = inner_ipv4_hdr->ttl;
|
|
|
|
inner_ipv4_len = IPNET_IP4_GET_HDR_LEN(inner_ipv4_hdr) * sizeof(Ip_u32);
|
|
inner_ipv4_proto = inner_ipv4_hdr->p;
|
|
|
|
if (outer_ipv4_from_addr != IP_NULL
|
|
&& IP_GET_32ON16(inner_ipv4_hdr->src) == outer_ipv4_from_addr->s_addr)
|
|
{
|
|
/* Same source address for inner and outer, don't set the S flag */
|
|
min_encap_len = IPNET_TUNNEL_MIN_ENCAP_S0_LEN;
|
|
min_encap_hdr = ipcom_pkt_push_front(pkt, IPNET_TUNNEL_MIN_ENCAP_S0_LEN - inner_ipv4_len);
|
|
min_encap_hdr->flags = 0;
|
|
}
|
|
else
|
|
{
|
|
Ip_u32 src;
|
|
|
|
min_encap_len = IPNET_TUNNEL_MIN_ENCAP_S1_LEN;
|
|
min_encap_hdr = ipcom_pkt_push_front(pkt, IPNET_TUNNEL_MIN_ENCAP_S1_LEN - inner_ipv4_len);
|
|
|
|
src = IP_GET_32ON16(inner_ipv4_hdr->src);
|
|
min_encap_hdr->original_dst_addr[0] = inner_ipv4_hdr->dst[0];
|
|
min_encap_hdr->original_dst_addr[1] = inner_ipv4_hdr->dst[1];
|
|
IP_SET_32ON16(min_encap_hdr->original_src_addr, src);
|
|
min_encap_hdr->flags = 0x80; /* Set the S flag */
|
|
}
|
|
|
|
min_encap_hdr->proto = inner_ipv4_proto;
|
|
min_encap_hdr->chksum = 0;
|
|
min_encap_hdr->chksum = ipcom_in_checksum(min_encap_hdr, min_encap_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_min_encap_input
|
|
*===========================================================================
|
|
* Description: Restores the full inner IPv4 header from the minimal
|
|
* encapsulation header.
|
|
* Parameters: netif - A tunnel interface.
|
|
* pkt - A inputed packet that has a minimal encapsulation header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_min_encap_input(Ipnet_netif *netif, Ipcom_pkt *pkt)
|
|
{
|
|
int len;
|
|
Ip_u32 dst;
|
|
Ip_u32 src;
|
|
Ipnet_pkt_min_encap *min_encap_hdr;
|
|
Ipnet_pkt_ip *ipv4_hdr;
|
|
Ipnet_pkt_ip *outer_ipv4_hdr;
|
|
|
|
outer_ipv4_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
min_encap_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
if (IP_BIT_ISFALSE(min_encap_hdr->flags, 0x80))
|
|
{
|
|
len = IPNET_TUNNEL_MIN_ENCAP_S0_LEN;
|
|
src = IP_GET_32ON16(outer_ipv4_hdr->src);
|
|
}
|
|
else
|
|
{
|
|
len = IPNET_TUNNEL_MIN_ENCAP_S1_LEN;
|
|
src = IP_GET_32ON16(min_encap_hdr->original_src_addr);
|
|
}
|
|
dst = IP_GET_32ON16(min_encap_hdr->original_dst_addr);
|
|
|
|
/* Restore the complete IPv4 header */
|
|
ipv4_hdr = ipcom_pkt_push_front(pkt, IPNET_IP_HDR_SIZE - len);
|
|
ipv4_hdr->p = min_encap_hdr->proto;
|
|
ipv4_hdr->ttl = outer_ipv4_hdr->ttl;
|
|
ipv4_hdr->tos = outer_ipv4_hdr->tos;
|
|
ipv4_hdr->v_hl = 0x45;
|
|
ipv4_hdr->len = (Ip_u16) ip_htons(ipcom_pkt_get_length(pkt));
|
|
ipv4_hdr->id = 0;
|
|
ipv4_hdr->off = 0;
|
|
IP_SET_32ON16(ipv4_hdr->src, src);
|
|
IP_SET_32ON16(ipv4_hdr->dst, dst);
|
|
ipv4_hdr->sum = 0;
|
|
ipv4_hdr->sum = ipcom_in_checksum(ipv4_hdr, IPNET_IP_HDR_SIZE);
|
|
|
|
if (netif->ipcom.link_tap)
|
|
netif->ipcom.link_tap(&netif->ipcom, pkt, IP_PACKET_HOST, ip_htons(IP_ETH_P_IP), pkt->start, pkt->start);
|
|
|
|
return ipnet_ip4_input(netif, pkt);
|
|
}
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_obj_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified outer IPv4 header.
|
|
* Parameters: netif - A tunnel interface.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_tunnel_ip4_obj_hash(Ipnet_netif *netif)
|
|
{
|
|
unsigned hash_val;
|
|
struct Ip_ip_tunnel_param *if_tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
|
|
hash_val = ipcom_hash_update(&if_tp->remote_addr.sin.sin_addr,
|
|
sizeof(if_tp->remote_addr.sin.sin_addr),
|
|
if_tp->protocol + netif->vr_index);
|
|
return ipcom_hash_update(&if_tp->local_addr.sin.sin_addr,
|
|
sizeof(if_tp->local_addr.sin.sin_addr),
|
|
hash_val);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_key_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified outer IP header.
|
|
* Parameters: outer_ip_hdr - Outer IP header.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_tunnel_ip4_key_hash(struct Ipnet_tunnel_ip4_key *key)
|
|
{
|
|
unsigned hash_val;
|
|
Ip_u8 proto;
|
|
Ipnet_pkt_ip *outer_ip_hdr = key->outer_ip_hdr;
|
|
|
|
/* The tunnel always uses IPIP since it can be used both for tunneling IPv4 and IPv6 */
|
|
proto = (outer_ip_hdr->p == IP_IPPROTO_IPV6 ? IP_IPPROTO_IPIP : outer_ip_hdr->p);
|
|
|
|
hash_val = ipcom_hash_update(outer_ip_hdr->src, sizeof(outer_ip_hdr->src), proto + key->vr);
|
|
return ipcom_hash_update(outer_ip_hdr->dst, sizeof(outer_ip_hdr->dst), hash_val);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_hash_cmp
|
|
*===========================================================================
|
|
* Description: Compares if the outer IP header matches the specified interface.
|
|
* Parameters: netif - An interface
|
|
* outer_ip_hdr - Outer IP header.
|
|
* Returns: IP_TRUE if the interface matches the outer IP header.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipnet_tunnel_ip4_hash_cmp(Ipnet_netif *netif, struct Ipnet_tunnel_ip4_key *key)
|
|
{
|
|
struct Ip_ip_tunnel_param *if_tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
Ipnet_pkt_ip *outer_ip_hdr = key->outer_ip_hdr;
|
|
Ip_u8 proto;
|
|
|
|
proto = (outer_ip_hdr->p == IP_IPPROTO_IPV6 ? IP_IPPROTO_IPIP : outer_ip_hdr->p);
|
|
return netif->vr_index == key->vr
|
|
&& if_tp->protocol == proto
|
|
&& ipcom_memcmp(&if_tp->local_addr.sin.sin_addr,
|
|
outer_ip_hdr->dst, sizeof(Ip_u32)) == 0
|
|
&& ipcom_memcmp(&if_tp->remote_addr.sin.sin_addr,
|
|
outer_ip_hdr->src, sizeof(Ip_u32)) == 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_tos
|
|
*===========================================================================
|
|
* Description: Extract the TOS bits from the inner header.
|
|
* Parameters: pkt - a packet
|
|
* Returns: The TOS bits.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_u8
|
|
ipnet_tunnel_tos(Ipcom_pkt *pkt)
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((pkt->data[pkt->start] & 0xf0) == 0x60)
|
|
return IPNET_IP6_GET_CLASS(((Ipnet_pkt_ip6 *) &pkt->data[pkt->start]));
|
|
#endif
|
|
#ifdef IPCOM_USE_INET
|
|
if ((pkt->data[pkt->start] & 0xf0) == 0x40)
|
|
return ((Ipnet_pkt_ip *)&pkt->data[pkt->start])->tos;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_tx
|
|
*===========================================================================
|
|
* Description: Tunnel packets over IPv4.
|
|
* Parameters: neigh - The neighbor for the next hop
|
|
* plink - The link parameters for this tunnel.
|
|
* proto - The protocol ID to use in the outer header.
|
|
* tos - TOS field to propagate to the outer header.
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_ip4_tx(Ipnet_neigh *neigh,
|
|
Ipnet_tunnel_plink *plink,
|
|
Ip_u8 proto,
|
|
Ip_u8 tos,
|
|
Ipcom_pkt *pkt)
|
|
{
|
|
int err;
|
|
Ipnet_tunnel_nat_t nat_t;
|
|
struct Ip_ip_tunnel_param *tp = &plink->tp;
|
|
Ip_u8 ttl = tp->ttl;
|
|
struct Ip_in_addr *from;
|
|
struct Ip_in_addr *to;
|
|
struct Ip_sockaddr_in *gw;
|
|
|
|
nat_t.type = IP_UDP_NAT_T_DISABLED;
|
|
|
|
/*
|
|
* Local address of outer header
|
|
*/
|
|
if (tp->local_addr.sin.sin_addr.s_addr == IP_INADDR_ANY)
|
|
from = IP_NULL;
|
|
else
|
|
{
|
|
from = &tp->local_addr.sin.sin_addr;
|
|
if (ipnet_ip4_get_addr_type(from->s_addr,
|
|
pkt->vr_index,
|
|
IP_NULL)
|
|
!= IPNET_ADDR_TYPE_UNICAST)
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"Tunnel %s: Erronous tunnel source address (non local) specified",
|
|
neigh->netif->ipcom.name);
|
|
err = -IP_ERRNO_EADDRNOTAVAIL;
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
if (IP_LIKELY((gw = (struct Ip_sockaddr_in *)neigh->neigh_data) == IP_NULL || gw->sin_family != IP_AF_INET))
|
|
gw = &tp->remote_addr.sin;
|
|
|
|
/*
|
|
* Remote address of outer header
|
|
*/
|
|
to = &gw->sin_addr;
|
|
if (to->s_addr == IP_INADDR_ANY)
|
|
{
|
|
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_BROADCAST | IPCOM_PKT_FLAG_MULTICAST))
|
|
err = 0;
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"Tunnel %s: No tunnel endpoint specified",
|
|
neigh->netif->ipcom.name);
|
|
err = -IP_ERRNO_ENETUNREACH;
|
|
}
|
|
goto errout;
|
|
}
|
|
|
|
/*
|
|
* The source port specified in the tunneling interface
|
|
* FIXME: NAT-T currently broken.
|
|
*/
|
|
nat_t.sport = tp->local_addr.sin.sin_port;
|
|
nat_t.dport = gw->sin_port;
|
|
nat_t.proto = &proto;
|
|
nat_t.cksum = IP_NULL;
|
|
|
|
/* Are we using any form of NAT-T translation? */
|
|
if (IP_UNLIKELY(nat_t.dport) != 0 && IP_UNLIKELY(nat_t.sport) != 0)
|
|
{
|
|
/* OK; We've got a UDP NAT-T translation; extract type */
|
|
nat_t.type = gw->sin_zero[0];
|
|
}
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
if (tp->protocol == IP_IPPROTO_MOBILE)
|
|
{
|
|
err = ipnet_tunnel_min_encap_create_header(pkt, from, &ttl);
|
|
if (err < 0)
|
|
goto errout;
|
|
else if (err > 0)
|
|
proto = IP_IPPROTO_IPIP;
|
|
}
|
|
#endif
|
|
|
|
if (IP_UNLIKELY(nat_t.type != IP_UDP_NAT_T_DISABLED))
|
|
{
|
|
err = ipnet_tunnel_nat_t(pkt, &nat_t);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
IPNET_TUNNEL_PKT_FLAGS_OUTPUT_UPDATE(pkt);
|
|
return ipnet_ip4_tx(tos,
|
|
ttl,
|
|
proto,
|
|
&from->s_addr,
|
|
&to->s_addr,
|
|
0,
|
|
pkt);
|
|
|
|
errout:
|
|
ipcom_pkt_free(pkt);
|
|
return err;
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_obj_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified outer IPv6 header.
|
|
* Parameters: netif - A tunnel interface.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_tunnel_ip6_obj_hash(Ipnet_netif *netif)
|
|
{
|
|
unsigned hash_val;
|
|
struct Ip_ip_tunnel_param *if_tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
|
|
hash_val = ipcom_hash_update(&if_tp->remote_addr.sin6.sin6_addr,
|
|
sizeof(if_tp->remote_addr.sin6.sin6_addr),
|
|
if_tp->protocol + netif->vr_index);
|
|
return ipcom_hash_update(&if_tp->local_addr.sin6.sin6_addr,
|
|
sizeof(if_tp->local_addr.sin6.sin6_addr),
|
|
hash_val);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_key_hash
|
|
*===========================================================================
|
|
* Description: Computes a hash value for the specified outer IP header.
|
|
* Parameters: outer_ip_hdr - Outer IP header.
|
|
* Returns: The hash value.
|
|
*
|
|
*/
|
|
IP_STATIC unsigned
|
|
ipnet_tunnel_ip6_key_hash(struct Ipnet_tunnel_ip6_key *key)
|
|
{
|
|
unsigned hash_val;
|
|
Ipnet_pkt_ip6 *outer_ip_hdr = key->outer_ip_hdr;
|
|
|
|
hash_val = ipcom_hash_update(&outer_ip_hdr->src,
|
|
sizeof(outer_ip_hdr->src),
|
|
key->ip_proto + key->vr);
|
|
return ipcom_hash_update(&outer_ip_hdr->dst, sizeof(outer_ip_hdr->dst), hash_val);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_hash_cmp
|
|
*===========================================================================
|
|
* Description: Compares if the outer IP header matches the specified interface.
|
|
* Parameters: netif - An interface
|
|
* outer_ip_hdr - Outer IP header.
|
|
* Returns: IP_TRUE if the interface matches the outer IP header.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_bool
|
|
ipnet_tunnel_ip6_hash_cmp(Ipnet_netif *netif, struct Ipnet_tunnel_ip6_key *key)
|
|
{
|
|
struct Ip_ip_tunnel_param *if_tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
Ipnet_pkt_ip6 *outer_ip_hdr = key->outer_ip_hdr;
|
|
|
|
return key->vr == netif->vr_index
|
|
&& if_tp->protocol == key->ip_proto
|
|
&& ipcom_memcmp(&if_tp->local_addr.sin6.sin6_addr,
|
|
&outer_ip_hdr->dst, sizeof(outer_ip_hdr->dst)) == 0
|
|
&& ipcom_memcmp(&if_tp->remote_addr.sin6.sin6_addr,
|
|
&outer_ip_hdr->src, sizeof(outer_ip_hdr->src)) == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_create_addr
|
|
*===========================================================================
|
|
* Description: Creates an automatic IPv6 address.
|
|
* Parameters: netif - A tunnel interface for which an address should be
|
|
* created.
|
|
* prefix - The first 64 bits of the address.
|
|
* in6_addr - Buffer where the created address will be stored.
|
|
* Returns: 0 (success)
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_ip6_create_addr(Ipnet_netif *netif,
|
|
IP_CONST struct Ip_in6_addr *prefix,
|
|
struct Ip_in6_addr *in6_addr)
|
|
{
|
|
Ipnet_netif *eth_netif = IP_NULL;
|
|
Ip_u32 i;
|
|
struct Ip_ip_tunnel_param *tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
|
|
in6_addr->in6.addr32[0] = prefix->in6.addr32[0];
|
|
in6_addr->in6.addr32[1] = prefix->in6.addr32[1];
|
|
|
|
if (tp->local_addr.sin.sin_addr.s_addr)
|
|
{
|
|
/* RFC 2893 chapter 3.1
|
|
...
|
|
The Interface Identifier [14] for such an Interface SHOULD be the
|
|
32-bit IPv4 address of that interface, with the bytes in the same
|
|
order in which they would appear in the header of an IPv4 packet,
|
|
padded at the left with zeros to a total of 64 bits.
|
|
... */
|
|
in6_addr->in6.addr32[2] = 0;
|
|
in6_addr->in6.addr32[3] = tp->local_addr.sin.sin_addr.s_addr;
|
|
return 0;
|
|
}
|
|
|
|
IPNET_NETIF_FOR_EACH(eth_netif, i)
|
|
{
|
|
if (eth_netif->ipcom.type == IP_IFT_ETHER)
|
|
break;
|
|
}
|
|
|
|
if (eth_netif != IP_NULL)
|
|
{
|
|
/* Use the MAC from an Ethernet interface */
|
|
in6_addr->in6.addr8[8] = eth_netif->ipcom.link_addr[0] ^ 0x02;
|
|
in6_addr->in6.addr8[9] = eth_netif->ipcom.link_addr[1];
|
|
in6_addr->in6.addr8[10] = eth_netif->ipcom.link_addr[2];
|
|
in6_addr->in6.addr8[11] = 0xff;
|
|
in6_addr->in6.addr8[12] = 0xfe;
|
|
in6_addr->in6.addr8[13] = eth_netif->ipcom.link_addr[3];
|
|
in6_addr->in6.addr8[14] = eth_netif->ipcom.link_addr[4];
|
|
in6_addr->in6.addr8[15] = eth_netif->ipcom.link_addr[5];
|
|
return 0;
|
|
}
|
|
|
|
/* Last resort: use a random number */
|
|
in6_addr->in6.addr32[2] = ipcom_random() & ip_htonl(~0x02000000);
|
|
in6_addr->in6.addr32[3] = ipcom_random();
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_output
|
|
*===========================================================================
|
|
* Description: Tunnel packets over IPv6.
|
|
* Parameters: neigh - neighbor entry for the next hop
|
|
* plink - The link parameters for this tunnel.
|
|
* nxt - The protocol ID to use in the outer header.
|
|
* tclass - Traffic class to propagate to outer header.
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_ip6_tx(Ipnet_neigh *neigh,
|
|
Ipnet_tunnel_plink *plink,
|
|
Ip_u8 nxt,
|
|
Ip_u8 tclass,
|
|
Ipcom_pkt *pkt)
|
|
{
|
|
int err;
|
|
struct Ip_ip_tunnel_param *tp = &plink->tp;
|
|
Ipnet_tunnel_nat_t nat_t;
|
|
Ip_u8 hlim = tp->ttl;
|
|
struct Ip_in6_addr *from;
|
|
struct Ip_in6_addr *to;
|
|
Ip_u32 zone_id;
|
|
struct Ip_sockaddr_in6 *gw;
|
|
|
|
nat_t.type = IP_UDP_NAT_T_DISABLED;
|
|
|
|
/*
|
|
* Determine the local address for the outer header.
|
|
*/
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(&tp->local_addr.sin6.sin6_addr))
|
|
from = IP_NULL;
|
|
else
|
|
{
|
|
from = &tp->local_addr.sin6.sin6_addr;
|
|
if (ipnet_ip6_get_addr_type(from,
|
|
pkt->vr_index,
|
|
IP_NULL)
|
|
!= IPNET_ADDR_TYPE_UNICAST)
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"Tunnel %s: Erronous tunnel source address (non local) specified",
|
|
neigh->netif->ipcom.name);
|
|
err = -IP_ERRNO_EADDRNOTAVAIL;
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
if (IP_LIKELY((gw = (struct Ip_sockaddr_in6 *)neigh->neigh_data) == IP_NULL || gw->sin6_family != IP_AF_INET6))
|
|
gw = &tp->remote_addr.sin6;
|
|
|
|
to = &gw->sin6_addr;
|
|
zone_id = gw->sin6_scope_id;
|
|
|
|
if (IP_IN6_IS_ADDR_UNSPECIFIED(to))
|
|
{
|
|
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_BROADCAST | IPCOM_PKT_FLAG_MULTICAST))
|
|
err = 0;
|
|
else
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"Tunnel %s: No tunnel endpoint specified",
|
|
neigh->netif->ipcom.name);
|
|
err = -IP_ERRNO_ENETUNREACH;
|
|
}
|
|
goto errout;
|
|
}
|
|
|
|
/*
|
|
* The source port specified in the tunneling interface
|
|
* FIXME: NAT-T currently broken.
|
|
*/
|
|
nat_t.sport = tp->local_addr.sin6.sin6_port;
|
|
nat_t.dport = gw->sin6_port;
|
|
nat_t.proto = &nxt;
|
|
if (IP_UNLIKELY(nat_t.sport != 0 && nat_t.dport != 0))
|
|
nat_t.type = gw->sin6_flowinfo;
|
|
else
|
|
nat_t.type = 0;
|
|
nat_t.cksum = IP_NULL;
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
if (tp->protocol == IP_IPPROTO_MOBILE)
|
|
{
|
|
err = ipnet_tunnel_min_encap_create_header(pkt, IP_NULL, &hlim);
|
|
if (err < 0)
|
|
goto errout;
|
|
else if (err > 0)
|
|
nxt = IP_IPPROTO_IPV6;
|
|
}
|
|
#endif
|
|
|
|
if (nat_t.type != IP_UDP_NAT_T_DISABLED)
|
|
{
|
|
err = ipnet_tunnel_nat_t(pkt, &nat_t);
|
|
if (err < 0)
|
|
goto errout;
|
|
}
|
|
|
|
IPNET_TUNNEL_PKT_FLAGS_OUTPUT_UPDATE(pkt);
|
|
return ipnet_ip6_tx(tclass,
|
|
hlim,
|
|
nxt,
|
|
gw->sin6_flowinfo,
|
|
from,
|
|
to,
|
|
zone_id,
|
|
0,
|
|
pkt);
|
|
|
|
errout:
|
|
ipcom_pkt_free(pkt);
|
|
return err;
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_tx
|
|
*===========================================================================
|
|
* Description: Tunnel device transmitt handler.
|
|
* Parameters: neigh - neighbor entry for the first hop.
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* inner IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_tx(Ipnet_neigh *neigh, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_netif *netif = neigh->netif;
|
|
Ipnet_tunnel_plink *plink = netif->ipcom.plink;
|
|
struct Ip_ip_tunnel_param *tp = &plink->tp;
|
|
int outer_proto = IP_IPPROTO_IPIP;
|
|
Ip_u8 tos;
|
|
unsigned ip_ver;
|
|
|
|
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_TUNNEL_TX);
|
|
|
|
ip_assert(tp->protocol == IP_IPPROTO_IPIP
|
|
|| tp->protocol == IP_IPPROTO_GRE
|
|
|| tp->protocol == IP_IPPROTO_MOBILE);
|
|
|
|
if (pkt->start < netif->ipcom.link_hdr_size)
|
|
/*
|
|
* Try to increase the header space, the sanity check will
|
|
* discard the packet is this fails
|
|
*/
|
|
(void) ipnet_increase_hdr_space(&pkt, IP_TRUE);
|
|
|
|
IPNET_TUNNEL_OUTPUT_SANITY_CHECK(netif, pkt);
|
|
|
|
ip_ver = *(Ip_u8*)ipcom_pkt_get_data(pkt, 0) >> 4;
|
|
|
|
/*
|
|
* Extract frame type; could leave this to the tap handler, but
|
|
* I'd rather do it explicitly
|
|
*/
|
|
if (netif->ipcom.link_tap)
|
|
{
|
|
switch (ip_ver)
|
|
{
|
|
case 4:
|
|
/* Let the tap handler copy the packet */
|
|
netif->ipcom.link_tap(&netif->ipcom,
|
|
pkt,
|
|
IP_PACKET_OUTGOING,
|
|
ip_htons(IP_ETH_P_IP),
|
|
pkt->ipstart,
|
|
pkt->ipstart);
|
|
break;
|
|
case 6:
|
|
netif->ipcom.link_tap(&netif->ipcom,
|
|
pkt,
|
|
IP_PACKET_OUTGOING,
|
|
ip_htons(IP_ETH_P_IPV6),
|
|
pkt->ipstart,
|
|
pkt->ipstart);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Extract the TOS or TCLASS field from the packet; Must do it
|
|
* now, since for example GRE adds its own headers to it, which
|
|
* obscures the IP header start for us
|
|
*/
|
|
tos = ipnet_tunnel_tos(pkt);
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (ip_ver == 6)
|
|
outer_proto = IP_IPPROTO_IPV6;
|
|
#endif
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
if (tp->protocol == IP_IPPROTO_GRE
|
|
|| tp->protocol == IP_IPPROTO_MOBILE)
|
|
{
|
|
Ip_u8 new_proto = tp->protocol;
|
|
|
|
/*
|
|
* Check for change in the IFF_LINK0 flag
|
|
*/
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK0))
|
|
{
|
|
if (tp->protocol == IP_IPPROTO_GRE)
|
|
new_proto = IP_IPPROTO_MOBILE;
|
|
}
|
|
else
|
|
{
|
|
if (tp->protocol == IP_IPPROTO_MOBILE)
|
|
new_proto = IP_IPPROTO_GRE;
|
|
}
|
|
|
|
if (new_proto != tp->protocol)
|
|
{
|
|
Ipcom_hash *tunnel_ifs = IP_NULL;
|
|
|
|
/*
|
|
* IFF_LINK0 flag has changed, update the IP protocol
|
|
*/
|
|
switch (tp->local_addr.sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
tunnel_ifs = ipnet->ip4.tunnel_ifs;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
tunnel_ifs = ipnet->ip6.tunnel_ifs;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (tunnel_ifs)
|
|
{
|
|
(void)ipcom_hash_remove(tunnel_ifs, netif);
|
|
tp->protocol = new_proto;
|
|
(void)ipcom_hash_add(tunnel_ifs, netif);
|
|
}
|
|
}
|
|
|
|
if (tp->protocol == IP_IPPROTO_GRE)
|
|
{
|
|
int ret;
|
|
|
|
ret = ipnet_gre_output_append(netif, tp, pkt);
|
|
if (ret < 0)
|
|
{
|
|
ipcom_pkt_free(pkt);
|
|
return ret;
|
|
}
|
|
outer_proto = IP_IPPROTO_GRE;
|
|
}
|
|
else
|
|
outer_proto = IP_IPPROTO_MOBILE;
|
|
}
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
netif->obytes += ipcom_pkt_get_length(pkt);
|
|
netif->opackets++;
|
|
|
|
switch (tp->local_addr.sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
return ipnet_tunnel_ip4_tx(neigh, plink, outer_proto, tos, pkt);
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
return ipnet_tunnel_ip6_tx(neigh, plink, outer_proto, tos, pkt);
|
|
#endif
|
|
default:
|
|
IP_PANIC2();
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_sit_tx
|
|
*===========================================================================
|
|
* Description: Tunnels IPv6 packets that uses IPv6-compatible-IPv4 addresses.
|
|
* Parameters: netif - The interface the packet was sent to.
|
|
* rt - The route followed.
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
#ifdef IPNET_USE_SIT
|
|
IP_STATIC int
|
|
ipnet_tunnel_sit_tx(Ipnet_neigh *neigh, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_ip6 *inner_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
Ipnet_netif *netif = neigh->netif;
|
|
Ip_u32 *from;
|
|
|
|
IPNET_TUNNEL_OUTPUT_SANITY_CHECK(netif, pkt);
|
|
|
|
if (IPNET_IP6_GET_VER(inner_hdr) != 6
|
|
|| IP_IN6_IS_ADDR_V4COMPAT(&inner_hdr->dst) == IP_FALSE)
|
|
{
|
|
ipcom_pkt_free(pkt);
|
|
return IP_IN6_IS_ADDR_MULTICAST(&inner_hdr->dst) ? 0 : -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
netif->obytes += ipcom_pkt_get_length(pkt);
|
|
netif->opackets++;
|
|
|
|
if (IP_IN6_IS_ADDR_V4COMPAT(&inner_hdr->src) == IP_FALSE)
|
|
from = IP_NULL;
|
|
else
|
|
from = &inner_hdr->src.in6.addr32[3];
|
|
|
|
IPNET_TUNNEL_PKT_FLAGS_OUTPUT_UPDATE(pkt);
|
|
|
|
/*
|
|
* The 'to' address for the outer header is defined by the 'dst'
|
|
* address of the inner header (::IPv4).
|
|
*/
|
|
return ipnet_ip4_tx(ipnet_tunnel_tos(pkt),
|
|
inner_hdr->hlim,
|
|
IP_IPPROTO_IPV6,
|
|
from,
|
|
&inner_hdr->dst.in6.addr32[3],
|
|
0,
|
|
pkt);
|
|
}
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_6to4_tx
|
|
*===========================================================================
|
|
* Description: Tunnels IPv6 packets that uses 6to4 (2002:IPv4/48) addresses.
|
|
* Parameters: neigh - the neighbor entry for the next hop.
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
#ifdef IPNET_USE_6TO4
|
|
IP_STATIC int
|
|
ipnet_tunnel_6to4_tx(Ipnet_neigh *neigh, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_ip6 *inner_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
Ipnet_netif *netif = neigh->netif;
|
|
struct Ip_in6_addr *next_hop = IP_NULL;
|
|
Ip_u32 from_storage;
|
|
Ip_u32 *from = IP_NULL;
|
|
|
|
IPNET_TUNNEL_OUTPUT_SANITY_CHECK(netif, pkt);
|
|
|
|
if (IPNET_IP6_GET_VER(inner_hdr) == 6)
|
|
{
|
|
next_hop = &inner_hdr->dst;
|
|
if (!IPNET_IP6_IS_ADDR_6TO4(&inner_hdr->dst))
|
|
{
|
|
Ipnet_route_entry *rt;
|
|
Ipnet_flow_spec flow_spec;
|
|
|
|
ipnet_ip6_flow_spec_from_pkt(&flow_spec,
|
|
pkt,
|
|
inner_hdr,
|
|
IP_FALSE);
|
|
|
|
/*
|
|
* Not sending directly to a 6to4 address, check if we are
|
|
* a 6to4 relay
|
|
*/
|
|
if (ipnet_route_lookup_ecmp(IP_AF_INET6,
|
|
pkt->vr_index,
|
|
0,
|
|
&inner_hdr->dst,
|
|
0,
|
|
netif->ipcom.ifindex,
|
|
IP_NULL,
|
|
&flow_spec,
|
|
&rt) >= 0)
|
|
{
|
|
if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_GATEWAY))
|
|
{
|
|
ip_assert(rt->gateway->sa_family == IP_AF_INET6);
|
|
next_hop = &((struct Ip_sockaddr_in6*) rt->gateway)->sin6_addr;
|
|
if (!IPNET_IP6_IS_ADDR_6TO4(next_hop))
|
|
next_hop = IP_NULL;
|
|
/* else: we are acting as a 6to4 relay */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (next_hop == IP_NULL)
|
|
{
|
|
ipcom_pkt_free(pkt);
|
|
return IP_IN6_IS_ADDR_MULTICAST(&inner_hdr->dst) ? 0 : -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
netif->obytes += ipcom_pkt_get_length(pkt);
|
|
netif->opackets++;
|
|
|
|
/*
|
|
* Add outer IPv4 header.
|
|
*/
|
|
if (IPNET_IP6_IS_ADDR_6TO4(&inner_hdr->src))
|
|
{
|
|
ipcom_memcpy(&from_storage,
|
|
&inner_hdr->src.in6.addr16[1],
|
|
sizeof(from_storage));
|
|
if (ipnet_ip4_get_addr_type(from_storage,
|
|
pkt->vr_index,
|
|
IP_NULL) == IPNET_ADDR_TYPE_UNICAST)
|
|
from = &from_storage;
|
|
}
|
|
|
|
IPNET_TUNNEL_PKT_FLAGS_OUTPUT_UPDATE(pkt);
|
|
|
|
/*
|
|
* The 'to' address for the outer header is defined by the 'dst'
|
|
* address of the inner header (2002:IPv4/48).
|
|
*/
|
|
return ipnet_ip4_tx(ipnet_tunnel_tos(pkt),
|
|
inner_hdr->hlim,
|
|
IP_IPPROTO_IPV6,
|
|
from,
|
|
(Ip_u32 *) &next_hop->in6.addr16[1],
|
|
0,
|
|
pkt);
|
|
}
|
|
#endif /* IPNET_USE_6TO4 */
|
|
|
|
|
|
#ifdef IPNET_USE_6OVER4
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_6over4_ipv6_to_ipv4_mcast_addr
|
|
*===========================================================================
|
|
* Description: Returns the IPv4 multicast address that should be used
|
|
* when sending packets to the specified IPv6 multicast address.
|
|
* Parameters: ipv4_mcast - The IPv4 multicast address to use.
|
|
* ipv6_mcast - An IPv6 multicast address.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_6over4_ipv6_to_ipv4_mcast_addr(struct Ip_in_addr *ipv4_mcast,
|
|
const struct Ip_in6_addr *ipv6_mcast)
|
|
{
|
|
Ip_u8 *ipv4_mcast_dst = (Ip_u8*) ipv4_mcast;
|
|
|
|
ipv4_mcast_dst[0] = 239;
|
|
ipv4_mcast_dst[1] = (Ip_u8) ipcom_sysvar_get_as_int0("ipnet.6over4.OLS", 192);
|
|
ipv4_mcast_dst[2] = ipv6_mcast->in6.addr8[14];
|
|
ipv4_mcast_dst[3] = ipv6_mcast->in6.addr8[15];
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_6over4_tx
|
|
*===========================================================================
|
|
* Description: Tunnels IPv6 packets using IPv4 as a virtual Ethernet.
|
|
* Parameters: neigh - the neighbor of the next hop
|
|
* pkt - The packet to loopback. pkt->start is the offset to the
|
|
* IP header.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_6over4_tx(Ipnet_neigh *neigh, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_ip6 *inner_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
Ipnet_netif *netif = neigh->netif;
|
|
Ipnet_tunnel_plink *plink = netif->ipcom.plink;
|
|
struct Ip_ip_tunnel_param *tp = &plink->tp;
|
|
struct Ip_in_addr ipv4_dst;
|
|
const struct Ip_in_addr *ipv4_src;
|
|
Ipnet_ip4_addr_entry *addr_entry;
|
|
|
|
IPNET_TUNNEL_OUTPUT_SANITY_CHECK(netif, pkt);
|
|
|
|
if (IPNET_IP6_GET_VER(inner_hdr) != 6)
|
|
{
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
netif->obytes += ipcom_pkt_get_length(pkt);
|
|
netif->opackets++;
|
|
|
|
if (IP_IN6_IS_ADDR_MULTICAST(&inner_hdr->dst))
|
|
ipnet_tunnel_6over4_ipv6_to_ipv4_mcast_addr(&ipv4_dst, &inner_hdr->dst);
|
|
else
|
|
/* The 2 bytes offset is because RFC 2529 chapter 5 states
|
|
* that the link address is
|
|
*
|
|
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
|
* | Type |Length | must be zero | IPv4 Address |
|
|
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
|
*
|
|
* which makes the link address
|
|
* 0:0:ipv4[0]:ipv4[1]:ipv4[2]:ipv4[3]
|
|
*/
|
|
ipv4_dst.s_addr = IP_GET_32ON16(&neigh->lladdr[2]);
|
|
|
|
ipv4_src = &tp->local_addr.sin.sin_addr;
|
|
addr_entry = ipnet_ip4_get_addr_entry(ipv4_src->s_addr,
|
|
netif->vr_index,
|
|
IP_NULL);
|
|
if (IP_UNLIKELY(addr_entry == IP_NULL))
|
|
{
|
|
/*
|
|
* The interface or the address of the physical interface has
|
|
* been removed
|
|
*/
|
|
IPCOM_LOG1(WARNING,
|
|
"Tunnel %s: Erronous tunnel source address (non local) specified",
|
|
netif->ipcom.name);
|
|
return -IP_ERRNO_EADDRNOTAVAIL;
|
|
}
|
|
|
|
IPNET_TUNNEL_PKT_FLAGS_OUTPUT_UPDATE(pkt);
|
|
|
|
return ipnet_ip4_tx(ipnet_tunnel_tos(pkt),
|
|
tp->ttl,
|
|
IP_IPPROTO_IPV6,
|
|
&ipv4_src->s_addr,
|
|
&ipv4_dst.s_addr,
|
|
addr_entry->netif->ipcom.ifindex,
|
|
pkt);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_6over4_create_addr
|
|
*===========================================================================
|
|
* Description: Creates an IPv6 address as part of the automatic address
|
|
* configuration.
|
|
* Parameters: netif - A 6over4 interface for which an address should be
|
|
* created.
|
|
* prefix - The first 64 bits of the address.
|
|
* in6_addr - Buffer where the created address will be stored.
|
|
* Returns: 0 (success)
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_6over4_create_addr(Ipnet_netif *netif,
|
|
IP_CONST struct Ip_in6_addr *prefix,
|
|
struct Ip_in6_addr *in6_addr)
|
|
{
|
|
struct Ip_ip_tunnel_param *tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
|
|
in6_addr->in6.addr32[0] = prefix->in6.addr32[0];
|
|
in6_addr->in6.addr32[1] = prefix->in6.addr32[1];
|
|
in6_addr->in6.addr32[2] = 0;
|
|
in6_addr->in6.addr32[3] = tp->local_addr.sin.sin_addr.s_addr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_6over4_join_leave_mcast_grp
|
|
*===========================================================================
|
|
* Description: Joins or leaves a specific IPv6 multicast group.
|
|
* Parameters: netif - A 6over4 interface on which the address will be
|
|
* joined/leaved.
|
|
* mcast_addr - The multicast address to join/leave.
|
|
* leave - IP_TRUE if the address should be leaved,
|
|
* IP_FALSE if the address should be joined.
|
|
* Returns: 0 = success, <0 = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_6over4_join_leave_mcast_grp(Ipnet_netif *netif,
|
|
struct Ip_in6_addr *mcast_addr,
|
|
Ip_bool leave)
|
|
{
|
|
struct Ip_in_addr ipv4_mcast;
|
|
Ipnet_netif *out_if;
|
|
Ipnet_ip4_addr_entry *addr_entry;
|
|
struct Ip_ip_tunnel_param *tp = IPNET_GET_TUNNEL_PARAM(netif);
|
|
|
|
addr_entry = ipnet_ip4_get_addr_entry(tp->local_addr.sin.sin_addr.s_addr,
|
|
netif->vr_index,
|
|
IP_NULL);
|
|
if (addr_entry == IP_NULL || addr_entry->type != IPNET_ADDR_TYPE_UNICAST)
|
|
{
|
|
IPCOM_LOG2(WARNING,
|
|
"6over4 tunnel: the address used as local IPv4 address (%s) for %s has been removed",
|
|
ipcom_inet_ntop(IP_AF_INET,
|
|
&tp->local_addr.sin.sin_addr,
|
|
ipnet->log_buf,
|
|
sizeof(ipnet->log_buf)),
|
|
netif->ipcom.name);
|
|
/*
|
|
* No local interface has the address specifed as source
|
|
*/
|
|
return -IP_ERRNO_EADDRNOTAVAIL;
|
|
}
|
|
|
|
out_if = addr_entry->netif;
|
|
ipnet_tunnel_6over4_ipv6_to_ipv4_mcast_addr(&ipv4_mcast, mcast_addr);
|
|
|
|
if (leave)
|
|
return ipnet_ip4_remove_addr(out_if, ipv4_mcast.s_addr);
|
|
return ipnet_ip4_add_addr(out_if, ipv4_mcast.s_addr, 0, IP_FALSE, IPNET_ADDR_TYPE_MULTICAST);
|
|
}
|
|
#endif /* IPNET_USE_6OVER4 */
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_sit_add_addresses
|
|
*===========================================================================
|
|
* Description: Adds an IPv4 compatible address for each IPv4 on all other
|
|
* interfaces.
|
|
* Parameters: netif - The tunnel interface to configure.
|
|
* other_netif - A network interface in the system.
|
|
* Returns:
|
|
*
|
|
*/
|
|
#ifdef IPNET_USE_SIT
|
|
IP_STATIC void
|
|
ipnet_tunnel_sit_add_addresses(Ipnet_netif *netif, Ipnet_netif *other_netif)
|
|
{
|
|
Ipnet_ip4_addr_entry *addr4;
|
|
struct Ip_in6_addr addr6 = IP_IN6ADDR_ANY_INIT;
|
|
|
|
if (other_netif == IP_NULL
|
|
|| netif == other_netif
|
|
|| IP_BIT_ISFALSE(other_netif->ipcom.flags, IP_IFF_UP))
|
|
/* Must only use addresses from active interfaces */
|
|
return;
|
|
|
|
for (addr4 = other_netif->inet4_addr_list;
|
|
addr4 != IP_NULL && addr4->type == IPNET_ADDR_TYPE_UNICAST;
|
|
addr4 = addr4->next)
|
|
{
|
|
addr6.in6.addr32[3] = addr4->ipaddr_n;
|
|
(void)ipnet_ip6_add_addr(netif,
|
|
&addr6,
|
|
IPNET_IP6_ADDR_FLAG_SIT,
|
|
IPCOM_ADDR_INFINITE,
|
|
IPCOM_ADDR_INFINITE,
|
|
96 + ipcom_mask_to_prefixlen(&addr4->netmask_n, 32));
|
|
}
|
|
}
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_configure
|
|
*===========================================================================
|
|
* Description: Performes automatic configuration for of the tunnel.
|
|
* Parameters: netif - The tunnel interface to configure.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_configure(Ipnet_netif *netif)
|
|
{
|
|
switch (ipnet_tunnel_type(netif))
|
|
{
|
|
#ifdef IPNET_USE_GRE
|
|
case IP_ARPHRD_IPGRE:
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_RUNNING))
|
|
(void)netif->link_ioctl(netif, IP_SIOCCHGTUNNEL, netif->ipcom.plink);
|
|
break;
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
#ifdef IPNET_USE_SIT
|
|
case IP_ARPHRD_SIT:
|
|
{
|
|
Ip_u32 i;
|
|
Ipnet_netif *iter_netif;
|
|
Ipnet_netif *loopback;
|
|
|
|
loopback = ipnet_loopback_get_netif(netif->vr_index);
|
|
|
|
IPNET_NETIF_FOR_EACH_ON_VR(iter_netif, netif->vr_index, i)
|
|
{
|
|
if (iter_netif == loopback)
|
|
continue;
|
|
ipnet_tunnel_sit_add_addresses(netif, iter_netif);
|
|
}
|
|
/* Add the loopback address last */
|
|
ipnet_tunnel_sit_add_addresses(netif, loopback);
|
|
}
|
|
break;
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_unconfigure
|
|
*===========================================================================
|
|
* Description: Removes any automatic configuration done for this tunnel.
|
|
* Parameters: netif - The tunnel interface to unconfigure.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_unconfigure(Ipnet_netif *netif)
|
|
{
|
|
switch (ipnet_tunnel_type(netif))
|
|
{
|
|
#ifdef IPNET_USE_SIT
|
|
case IP_ARPHRD_SIT:
|
|
{
|
|
Ipnet_ip6_addr_entry *addr;
|
|
|
|
while (IP_NULL != (addr = netif->inet6_addr_list))
|
|
(void)ipnet_ip6_remove_addr(netif, &addr->addr);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ioctl
|
|
*===========================================================================
|
|
* Description: IO control. Accept join/leave from multicast groups.
|
|
* Parameters: netif - The network interface on which to perform the operation.
|
|
* command - The command to perform.
|
|
* data - Data associated with the command.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_ioctl(Ipnet_netif *netif, Ip_u32 command, void *data)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
struct Ip_ifreq *ifreq = data;
|
|
struct Ip_sockaddr_in *peer_sin = (struct Ip_sockaddr_in *) &ifreq->ip_ifr_dstaddr;
|
|
#endif
|
|
Ipnet_tunnel_plink *plink = netif->ipcom.plink;
|
|
struct Ip_ip_tunnel_param *if_tp = &plink->tp;
|
|
struct Ip_ip_tunnel_param *arg_tp = data;
|
|
struct Ip_ip_tunnel_param iparg;
|
|
Ipnet_netif *outgoing_if = IP_NULL;
|
|
int ret = 0;
|
|
Ipcom_hash *tunnel_ifs = IP_NULL;
|
|
|
|
switch (if_tp->local_addr.sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
tunnel_ifs = ipnet->ip4.tunnel_ifs;
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
tunnel_ifs = ipnet->ip6.tunnel_ifs;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (command)
|
|
{
|
|
case IP_SIOCXOPEN:
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4
|
|
&& if_tp->local_addr.sa.sa_family != IP_AF_INET)
|
|
{
|
|
IPCOM_LOG1(WARNING,
|
|
"6over4 tunnel: the local address must be set and have type AF_INET on %s",
|
|
netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
ipnet_tunnel_configure(netif);
|
|
ipnet_kioevent(netif, IP_EIOXUP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
break;
|
|
|
|
case IP_SIOCXCLOSE:
|
|
ipnet_tunnel_unconfigure(netif);
|
|
ipnet_kioevent(netif, IP_EIOXDOWN, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
break;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_SIOCXADDMULTI_IN:
|
|
case IP_SIOCXDELMULTI_IN:
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_SIOCXADDMULTI_IN6:
|
|
case IP_SIOCXDELMULTI_IN6:
|
|
#ifdef IPNET_USE_6OVER4
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4)
|
|
ret = ipnet_tunnel_6over4_join_leave_mcast_grp(netif,
|
|
data,
|
|
command == IP_SIOCXDELMULTI_IN6);
|
|
#endif
|
|
#endif /* IPCOM_USE_INET6 */
|
|
break;
|
|
|
|
case IP_X_SIOCGETTUNNEL:
|
|
/* Linux style CHGTUNNEL */
|
|
|
|
switch (netif->ipcom.arphrd_type)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_ARPHRD_SIT:
|
|
case IP_ARPHRD_IPGRE:
|
|
case IP_ARPHRD_TUNNEL:
|
|
{
|
|
struct Ip_ip_tunnel_parm *tp4 = ifreq->ifr_ifru.ifru_data;
|
|
ipcom_strcpy(tp4->name, netif->ipcom.name);
|
|
ipcom_memset(&tp4->iph, 0, sizeof(tp4->iph));
|
|
tp4->iph.ttl = if_tp->ttl;
|
|
tp4->iph.protocol = if_tp->protocol;
|
|
tp4->iph.frag_off = ip_htons(IPNET_OFF_DF);
|
|
tp4->o_flags = if_tp->o_flags;
|
|
tp4->i_flags = if_tp->i_flags;
|
|
tp4->iph.saddr = if_tp->local_addr.sin.sin_addr.s_addr;
|
|
tp4->iph.daddr = if_tp->remote_addr.sin.sin_addr.s_addr;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_ARPHRD_TUNNEL6:
|
|
case IP_ARPHRD_IPGRE6:
|
|
{
|
|
struct Ip_ip6_tnl_parm *tp6 = ifreq->ifr_ifru.ifru_data;
|
|
|
|
ipcom_memset(tp6, 0, sizeof(*tp6));
|
|
ipcom_strcpy(tp6->name, netif->ipcom.name);
|
|
tp6->proto = IP_IPPROTO_IPV6;
|
|
|
|
IPNET_IP6_SET_ADDR(&tp6->raddr, &if_tp->remote_addr.sin6.sin6_addr);
|
|
IPNET_IP6_SET_ADDR(&tp6->laddr, &if_tp->local_addr.sin6.sin6_addr);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
break;
|
|
|
|
case IP_SIOCGETTUNNEL:
|
|
ipcom_memcpy(arg_tp, if_tp, sizeof(struct Ip_ip_tunnel_param));
|
|
break;
|
|
|
|
case IP_X_SIOCCHGTUNNEL:
|
|
/* Linux style CHGTUNNEL */
|
|
|
|
switch (netif->ipcom.arphrd_type)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_ARPHRD_SIT:
|
|
case IP_ARPHRD_IPGRE:
|
|
case IP_ARPHRD_TUNNEL:
|
|
{
|
|
struct Ip_ip_tunnel_parm *tp4 = ifreq->ifr_ifru.ifru_data;
|
|
ipcom_memset(&iparg, 0, sizeof(iparg));
|
|
ipcom_strcpy(iparg.ifr_name, netif->ipcom.name);
|
|
|
|
iparg.ttl = tp4->iph.ttl;
|
|
iparg.protocol = tp4->iph.protocol;
|
|
iparg.o_flags = tp4->o_flags;
|
|
iparg.i_flags = tp4->i_flags;
|
|
|
|
iparg.local_addr.sin.sin_family = IP_AF_INET;
|
|
IPCOM_SA_LEN_SET(&iparg.local_addr.sin, sizeof(struct Ip_sockaddr_in));
|
|
iparg.local_addr.sin.sin_addr.s_addr = tp4->iph.saddr;
|
|
|
|
iparg.remote_addr.sin.sin_family = IP_AF_INET;
|
|
IPCOM_SA_LEN_SET(&iparg.remote_addr.sin, sizeof(struct Ip_sockaddr_in));
|
|
iparg.remote_addr.sin.sin_addr.s_addr = tp4->iph.daddr;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_ARPHRD_TUNNEL6:
|
|
case IP_ARPHRD_IPGRE6:
|
|
{
|
|
struct Ip_ip6_tnl_parm *tp6 = ifreq->ifr_ifru.ifru_data;
|
|
|
|
ipcom_memset(&iparg, 0, sizeof(iparg));
|
|
ipcom_strcpy(iparg.ifr_name, netif->ipcom.name);
|
|
|
|
iparg.protocol = tp6->proto;
|
|
|
|
iparg.local_addr.sin6.sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&iparg.local_addr.sin6, sizeof(struct Ip_sockaddr_in6));
|
|
IPNET_IP6_SET_ADDR(&iparg.local_addr.sin6.sin6_addr, &tp6->laddr);
|
|
|
|
iparg.remote_addr.sin6.sin6_family = IP_AF_INET6;
|
|
IPCOM_SA_LEN_SET(&iparg.remote_addr.sin6, sizeof(struct Ip_sockaddr_in6));
|
|
IPNET_IP6_SET_ADDR(&iparg.remote_addr.sin6.sin6_addr, &tp6->raddr);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
arg_tp = &iparg;
|
|
|
|
/* FALL THROUGH */
|
|
case IP_SIOCCHGTUNNEL:
|
|
switch (netif->ipcom.arphrd_type)
|
|
{
|
|
case IP_ARPHRD_NONE:
|
|
case IP_ARPHRD_SIT:
|
|
case IP_ARPHRD_TUNNEL6TO4:
|
|
IPCOM_LOG1(DEBUG, "Tunnel: no parameters can be changed on the %s interface", netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
case IP_ARPHRD_TUNNEL6OVER4:
|
|
if (arg_tp->local_addr.sa.sa_family != IP_AF_INET)
|
|
{
|
|
IPCOM_LOG2(WARNING, "Tunnel: the local address must have domain IP_AF_INET, was %d != %d on %s",
|
|
arg_tp->local_addr.sa.sa_family, netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
tunnel_ifs = IP_NULL;
|
|
break;
|
|
default:
|
|
if (arg_tp->local_addr.sa.sa_family != arg_tp->remote_addr.sa.sa_family)
|
|
{
|
|
IPCOM_LOG3(WARNING, "Tunnel: the local and remote address must have the same domain %d != %d on %s",
|
|
arg_tp->local_addr.sa.sa_family, arg_tp->remote_addr.sa.sa_family, netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Outgoing interface is used to get reasonable value for MTU,
|
|
* the local address is also verified to be a valid unicast
|
|
* address if the tunnel type is 6over4
|
|
*/
|
|
switch (arg_tp->local_addr.sa.sa_family)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_AF_INET:
|
|
if (arg_tp->local_addr.sin.sin_addr.s_addr != IP_INADDR_ANY
|
|
|| netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4)
|
|
{
|
|
Ipnet_ip4_addr_entry *addr_entry;
|
|
|
|
addr_entry = ipnet_ip4_get_addr_entry(arg_tp->local_addr.sin.sin_addr.s_addr,
|
|
netif->vr_index,
|
|
IP_NULL);
|
|
if (addr_entry == IP_NULL || addr_entry->type != IPNET_ADDR_TYPE_UNICAST)
|
|
/* No local interface has the address specifed as source */
|
|
return -IP_ERRNO_EADDRNOTAVAIL;
|
|
outgoing_if = addr_entry->netif;
|
|
}
|
|
break;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4)
|
|
{
|
|
IPCOM_LOG1(ERR, "A 6over4 (%s) tunnel must have an IPv4 unicast local address",
|
|
netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
if (!IP_IN6_IS_ADDR_UNSPECIFIED(&arg_tp->local_addr.sin6.sin6_addr))
|
|
{
|
|
Ipnet_ip6_addr_entry *addr_entry;
|
|
|
|
addr_entry = ipnet_ip6_get_addr_entry(&arg_tp->local_addr.sin6.sin6_addr,
|
|
netif->vr_index,
|
|
IP_NULL);
|
|
if (addr_entry == IP_NULL || addr_entry->type != IPNET_ADDR_TYPE_UNICAST)
|
|
/* No local interface has the address specifed as source */
|
|
return -IP_ERRNO_EADDRNOTAVAIL;
|
|
outgoing_if = addr_entry->netif;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
IPCOM_LOG2(ERR, "Unsupported local address family %d for tunnel %s",
|
|
arg_tp->local_addr.sa.sa_family, netif->ipcom.name);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
if (tunnel_ifs != IP_NULL)
|
|
(void) ipcom_hash_remove(tunnel_ifs, netif);
|
|
|
|
ipcom_memmove(&if_tp->local_addr,
|
|
&arg_tp->local_addr,
|
|
sizeof(if_tp->local_addr));
|
|
|
|
if (netif->ipcom.arphrd_type != IP_ARPHRD_TUNNEL6OVER4)
|
|
ipcom_memmove(&if_tp->remote_addr,
|
|
&arg_tp->remote_addr,
|
|
sizeof(if_tp->remote_addr));
|
|
else
|
|
{
|
|
if_tp->remote_addr.sa.sa_family = IP_AF_UNSPEC;
|
|
IPCOM_SA_LEN_SET(&if_tp->remote_addr.sa, sizeof(struct Ip_sockaddr));
|
|
}
|
|
/* else: remote address does not make sense for a 6over4 tunnel */
|
|
|
|
/*
|
|
* Note: NetBSD uses 1280, Linux 1480 when tunneling over IPv4
|
|
* and 1460 when tunneling over IPv6
|
|
*/
|
|
ip_assert(netif->ipcom.arphrd_type != IP_ARPHRD_TUNNEL6OVER4 || outgoing_if != IP_NULL);
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4
|
|
|| outgoing_if != IP_NULL)
|
|
netif->ipcom.mtu = outgoing_if->ipcom.mtu;
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_IPGRE ||
|
|
netif->ipcom.arphrd_type == IP_ARPHRD_IPGRE6)
|
|
{
|
|
/* Note: The usage of the IFF_LINK0 flag is inverted compared to NetBSD */
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK0))
|
|
if_tp->protocol = IP_IPPROTO_MOBILE;
|
|
else
|
|
if_tp->protocol = IP_IPPROTO_GRE;
|
|
|
|
if (if_tp->protocol == IP_IPPROTO_GRE)
|
|
{
|
|
/*
|
|
* Note: NetBSD uses 1450, Linux 1476 (which is
|
|
* maximum on Eth)
|
|
*/
|
|
netif->ipcom.mtu -= 4;
|
|
if (IP_BIT_ISSET(if_tp->o_flags, IPCOM_TUNNEL_FLAG_GRE_CHECKSUM))
|
|
netif->ipcom.mtu -= 4;
|
|
}
|
|
}
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (arg_tp->local_addr.sa.sa_family == IP_AF_INET)
|
|
{
|
|
#ifdef IPNET_USE_6OVER4
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ipnet->netif_6over4_count; i++)
|
|
if (netif == ipnet->netif_6over4[i])
|
|
break;
|
|
if (i == ipnet->netif_6over4_count)
|
|
{
|
|
void *ptr;
|
|
ptr = ipcom_realloc(ipnet->netif_6over4,
|
|
(ipnet->netif_6over4_count + 1) * sizeof(Ipnet_netif *));
|
|
if (ptr == IP_NULL)
|
|
return -IP_ERRNO_ENOMEM;
|
|
|
|
ipnet->netif_6over4 = ptr;
|
|
ipnet->netif_6over4[ipnet->netif_6over4_count++] = netif;
|
|
}
|
|
netif->ipcom.link_index = outgoing_if->ipcom.ifindex;
|
|
/*
|
|
* Two byte is zero and the next 4 for is an IPv4
|
|
* address
|
|
*/
|
|
netif->ipcom.link_addr_size = 6;
|
|
netif->ipcom.link_addr[0] = 0;
|
|
netif->ipcom.link_addr[1] = 0;
|
|
ipcom_memcpy(&netif->ipcom.link_addr[2],
|
|
&if_tp->local_addr.sin.sin_addr,
|
|
sizeof(if_tp->local_addr.sin.sin_addr));
|
|
if_tp->ttl = (Ip_u8) ipcom_sysvar_get_as_int0("ipnet.6over4.BaseHopLimit", 8);
|
|
}
|
|
else
|
|
#endif /* IPNET_USE_6OVER4 */
|
|
{
|
|
if (ipnet->ip4.tunnel_ifs == IP_NULL)
|
|
{
|
|
ipnet->ip4.tunnel_ifs =
|
|
ipcom_hash_new((Ipcom_hash_obj_func) ipnet_tunnel_ip4_obj_hash,
|
|
(Ipcom_hash_key_func) ipnet_tunnel_ip4_key_hash,
|
|
(Ipcom_hash_cmp_func) ipnet_tunnel_ip4_hash_cmp);
|
|
}
|
|
tunnel_ifs = ipnet->ip4.tunnel_ifs;
|
|
if_tp->ttl = netif->conf.inet.base_hop_limit;
|
|
}
|
|
|
|
netif->ipcom.mtu -= IPNET_IP_HDR_SIZE;
|
|
netif->ipcom.link_hdr_size = IPNET_IP_HDR_SIZE;
|
|
}
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (arg_tp->local_addr.sa.sa_family == IP_AF_INET6)
|
|
{
|
|
if (ipnet->ip6.tunnel_ifs == IP_NULL)
|
|
{
|
|
ipnet->ip6.tunnel_ifs =
|
|
ipcom_hash_new((Ipcom_hash_obj_func) ipnet_tunnel_ip6_obj_hash,
|
|
(Ipcom_hash_key_func) ipnet_tunnel_ip6_key_hash,
|
|
(Ipcom_hash_cmp_func) ipnet_tunnel_ip6_hash_cmp);
|
|
}
|
|
tunnel_ifs = ipnet->ip6.tunnel_ifs;
|
|
netif->ipcom.mtu -= IPNET_IP6_HDR_SIZE;
|
|
netif->ipcom.link_hdr_size = IPNET_IP6_HDR_SIZE;
|
|
|
|
if_tp->ttl = netif->conf.inet6.base_hop_limit;
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
switch (netif->ipcom.arphrd_type)
|
|
{
|
|
#ifdef IPNET_USE_GRE
|
|
case IP_ARPHRD_IPGRE:
|
|
case IP_ARPHRD_IPGRE6:
|
|
netif->ipcom.link_hdr_size += IPNET_GRE_MAX_HDR_SIZE;
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (arg_tp->ttl)
|
|
if_tp->ttl = arg_tp->ttl;
|
|
if_tp->o_flags = arg_tp->o_flags;
|
|
if_tp->i_flags = arg_tp->i_flags;
|
|
|
|
if (netif->ipcom.arphrd_type != IP_ARPHRD_TUNNEL6OVER4)
|
|
{
|
|
int r = 0;
|
|
if (tunnel_ifs == IP_NULL
|
|
|| (r = ipcom_hash_add(tunnel_ifs, netif)) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG2(ERR,
|
|
"Failed to configure tunnel interface %s, resulting in hash error code %u",
|
|
netif->ipcom.name,
|
|
r);
|
|
|
|
ret = -IP_ERRNO_ENOMEM;
|
|
goto conf_errout;
|
|
}
|
|
}
|
|
|
|
netif->ipcom.mtu = IP_MAX(netif->ipcom.mtu, 1280);
|
|
ipnet_kioevent(netif, IP_EIOXRUNNING, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
break;
|
|
|
|
conf_errout:
|
|
ipnet_kioevent(netif, IP_EIOXSTOP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
break;
|
|
|
|
case IP_X_SIOCDELTUNNEL:
|
|
case IP_SIOCIFDESTROY:
|
|
{
|
|
Ip_bool was_running = IP_FALSE;
|
|
if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_RUNNING))
|
|
{
|
|
was_running = IP_TRUE;
|
|
ipnet_kioevent(netif, IP_EIOXSTOP, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
}
|
|
#ifndef IP_PORT_LKM
|
|
ret = ipcom_if_detach(&netif->ipcom);
|
|
if (ret < 0)
|
|
{
|
|
if (was_running)
|
|
ipnet_kioevent(netif, IP_EIOXRUNNING, IP_NULL, IP_FLAG_FC_STACKCONTEXT);
|
|
return ret;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_IPGRE || netif->ipcom.arphrd_type == IP_ARPHRD_IPGRE6)
|
|
ipnet_gre_destroy(netif);
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
#ifdef IPNET_USE_6OVER4
|
|
if (netif->ipcom.arphrd_type == IP_ARPHRD_TUNNEL6OVER4)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ipnet->netif_6over4_count; i++)
|
|
if (netif == ipnet->netif_6over4[i])
|
|
ipnet->netif_6over4[i] = ipnet->netif_6over4[--ipnet->netif_6over4_count];
|
|
if (ipnet->netif_6over4_count == 0)
|
|
{
|
|
ipcom_free(ipnet->netif_6over4);
|
|
ipnet->netif_6over4 = IP_NULL;
|
|
}
|
|
}
|
|
else
|
|
#endif /* IPNET_USE_6OVER4 */
|
|
if (tunnel_ifs != IP_NULL)
|
|
{
|
|
(void) ipcom_hash_remove(tunnel_ifs, netif);
|
|
if (tunnel_ifs->elem == 0)
|
|
{
|
|
ipcom_hash_delete(tunnel_ifs);
|
|
#ifdef IPCOM_USE_INET
|
|
if (if_tp->local_addr.sa.sa_family == IP_AF_INET)
|
|
ipnet->ip4.tunnel_ifs = IP_NULL;
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (if_tp->local_addr.sa.sa_family == IP_AF_INET6)
|
|
ipnet->ip6.tunnel_ifs = IP_NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ipnet_if_clean_ppp_peer(&plink->peer);
|
|
#ifndef IP_PORT_LKM
|
|
ret = ipcom_if_free(&netif->ipcom);
|
|
#endif
|
|
break;
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_SIOCGIFDSTADDR:
|
|
ipcom_memset(peer_sin, 0, sizeof(struct Ip_sockaddr_in));
|
|
peer_sin->sin_family = IP_AF_INET;
|
|
IPCOM_SA_LEN_SET(peer_sin, sizeof(struct Ip_sockaddr_in));
|
|
ipcom_memcpy(&peer_sin->sin_addr,
|
|
&plink->peer.peer4,
|
|
sizeof(struct Ip_in_addr));
|
|
break;
|
|
|
|
case IP_SIOCSIFDSTADDR:
|
|
if (IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_POINTOPOINT)
|
|
|| peer_sin->sin_family != IP_AF_INET
|
|
|| IPCOM_SA_LEN_GET(peer_sin) < sizeof(struct Ip_sockaddr_in))
|
|
return -IP_ERRNO_EINVAL;
|
|
ret = ipnet_if_set_ipv4_ppp_peer(&plink->peer, &peer_sin->sin_addr);
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
case IP_SIOCGIFHWADDR:
|
|
/* Switch to our if type */
|
|
ifreq->ip_ifr_addr.sa_family = (Ip_sa_family_t)netif->ipcom.arphrd_type;
|
|
break;
|
|
|
|
default:
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_rx
|
|
*===========================================================================
|
|
* Description: Generic reception handler for tunnel device
|
|
* Parameters: netif - a tunnel device
|
|
* pkt - packet with the outer IP header removed
|
|
* Returns: 0 = success
|
|
* <0 = error code
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_rx(Ipnet_netif *netif, Ipcom_pkt *pkt)
|
|
{
|
|
int outer_proto = -1;
|
|
int family = IP_AF_INET;
|
|
|
|
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_TUNNEL_RX);
|
|
|
|
if (IP_UNLIKELY(IP_BIT_ISFALSE(netif->ipcom.flags, IP_IFF_UP)))
|
|
{
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_EHOSTUNREACH;
|
|
}
|
|
|
|
if (IP_UNLIKELY(ipcom_pkt_get_length(pkt) < IPNET_IP_HDR_SIZE))
|
|
{
|
|
/*
|
|
* Cannot be a valid IPv4 or IPv6 header that follows the
|
|
* outer header. Note: the proper validation of the inner IP
|
|
* header will be done in ipnet_ip[4|6]_input(). This check is
|
|
* done just to make sure that it is at least safe to read the
|
|
* IP protocol version if the inner header.
|
|
*/
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
netif->ibytes += ipcom_pkt_get_length(pkt);
|
|
netif->ipackets++;
|
|
|
|
pkt->ifindex = netif->ipcom.ifindex;
|
|
pkt->vr_index = netif->vr_index;
|
|
|
|
/* Store the outer header offsets */
|
|
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_TUNNELED);
|
|
|
|
/* Setup the LLA offsets */
|
|
ipcom_pkt_set_int(pkt, IPNET_PKT_INFO_TUNNEL_IPSTART, pkt->ipstart, int);
|
|
|
|
/* Store the offsets */
|
|
switch (pkt->data[pkt->ipstart] & 0xf0)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case 0x40:
|
|
pkt->lla_off = ip_offsetof(Ipnet_pkt_ip, src) + pkt->ipstart;
|
|
pkt->lla_size = 4;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
case 0x60:
|
|
pkt->lla_off = ip_offsetof(Ipnet_pkt_ip6, src) + pkt->ipstart;
|
|
pkt->lla_size = sizeof(struct Ip_in6_addr);
|
|
family = IP_AF_INET6;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
#ifdef IPCOM_USE_PACKET
|
|
/* Give a copy of packet to all matching packet sockets. */
|
|
if (ipnet->num_packet_sock > 0
|
|
&& ipnet_packet_input(netif, pkt) == IP_FALSE)
|
|
return 0;
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if (family == IP_AF_INET)
|
|
{
|
|
Ipnet_pkt_ip *ippkt = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
ipcom_pkt_create_info(pkt,
|
|
IPNET_PKT_INFO_ENCAP,
|
|
sizeof(struct Ip_in_addr),
|
|
&ippkt->src[0]);
|
|
outer_proto = ippkt->p;
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if (family == IP_AF_INET6)
|
|
{
|
|
Ipnet_pkt_ip6 *ippkt = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
ipcom_pkt_create_info(pkt,
|
|
IPNET_PKT_INFO_ENCAP,
|
|
sizeof(struct Ip_in6_addr),
|
|
&ippkt->src);
|
|
outer_proto = ippkt->nxt;
|
|
}
|
|
#endif
|
|
|
|
switch (outer_proto)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case IP_IPPROTO_IPIP:
|
|
if (netif->ipcom.link_tap)
|
|
netif->ipcom.link_tap(&netif->ipcom,
|
|
pkt,
|
|
IP_PACKET_HOST,
|
|
ip_htons(IP_ETH_P_IP),
|
|
pkt->start,
|
|
pkt->start);
|
|
return ipnet_ip4_input(netif, pkt);
|
|
#endif
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_IPPROTO_IPV6:
|
|
if (netif->ipcom.link_tap)
|
|
netif->ipcom.link_tap(&netif->ipcom,
|
|
pkt,
|
|
IP_PACKET_HOST,
|
|
ip_htons(IP_ETH_P_IPV6),
|
|
pkt->start,
|
|
pkt->start);
|
|
return ipnet_ip6_input(netif, pkt);
|
|
#endif
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
case IP_IPPROTO_GRE:
|
|
return ipnet_gre_input(netif, pkt);
|
|
|
|
case IP_IPPROTO_MOBILE:
|
|
return ipnet_tunnel_min_encap_input(netif, pkt);
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
IP_PANIC2();
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_eth_get_frame_info
|
|
*===========================================================================
|
|
* Description: Gets header information from the frame header.
|
|
* Parameters: netif - A pointer to the network interface structure for this
|
|
* driver.
|
|
* pkt - A compleate link layer fram. pkt->start is the offset
|
|
* to the link layer header.
|
|
* pkt_type - Will be set to one of the IP_PACKET_xxx type,
|
|
* can be IP_NULL if this value is not needed.
|
|
* frame_type - Will be set to the frame type (i.e. the IP proto field).
|
|
* can be IP_NULL if this value is not needed.
|
|
* src - Will be set to the source address of the frame, can
|
|
* be IP_NULL.
|
|
* dst - Will be set to the destination address of the frame,
|
|
* can be IP_NULL.
|
|
* Returns: 0 = success, <0 = error code.
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipnet_tunnel_get_frame_info(Ipnet_netif *netif,
|
|
Ipcom_pkt *pkt,
|
|
Ip_u8 *pkt_type,
|
|
Ip_u16 *frame_type,
|
|
Ip_u8 *src,
|
|
Ip_u8 *dst)
|
|
{
|
|
IPCOM_UNUSED_ARG(netif);
|
|
ip_assert(pkt != IP_NULL);
|
|
|
|
switch (pkt->data[pkt->start] & 0xf0)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
case 0x40:
|
|
{
|
|
Ipnet_pkt_ip *ipv4 = (Ipnet_pkt_ip *) &pkt->data[pkt->start];
|
|
|
|
if (frame_type != IP_NULL)
|
|
*frame_type = ipv4->p;
|
|
|
|
if (src != IP_NULL)
|
|
ipcom_memcpy(src, ipv4->src, sizeof(struct Ip_in_addr));
|
|
if (dst != IP_NULL)
|
|
ipcom_memcpy(dst, ipv4->dst, sizeof(struct Ip_in_addr));
|
|
|
|
}
|
|
break;
|
|
#endif /* IPCOM_USE_INET */
|
|
#ifdef IPCOM_USE_INET6
|
|
case 0x60:
|
|
{
|
|
Ipnet_pkt_ip6 *ipv6 = (Ipnet_pkt_ip6 *) &pkt->data[pkt->start];
|
|
|
|
if (frame_type != IP_NULL)
|
|
*frame_type = ipv6->nxt;
|
|
|
|
if (src != IP_NULL)
|
|
ipcom_memcpy(src, &ipv6->src, sizeof(struct Ip_in6_addr));
|
|
if (dst != IP_NULL)
|
|
ipcom_memcpy(dst, &ipv6->dst, sizeof(struct Ip_in6_addr));
|
|
}
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
return -IP_ERRNO_EPROTONOSUPPORT;
|
|
}
|
|
|
|
if (pkt_type != IP_NULL)
|
|
*pkt_type = IP_PACKET_HOST;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_rx
|
|
*===========================================================================
|
|
* Description: Reception handler for packets tunneled over IPv4
|
|
* (IPIP, IPV6, GRE).
|
|
* Parameters: dst - destination cache entry for the outer header.
|
|
* pkt - packet data.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_ip4_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_ip *outer_ip_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
Ipnet_netif *netif = IP_NULL;
|
|
|
|
if (IP_IN_CLASSD(dst->flow_spec.to.in.s_addr))
|
|
/*
|
|
* Set the link multicast since the outer IPv4 header is the
|
|
* link layer for the inner IP header
|
|
*/
|
|
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_LINK_MULTICAST);
|
|
|
|
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV4);
|
|
|
|
IPCOM_PKT_RESET_CHECKSUM(pkt);
|
|
if (ipnet->ip4.tunnel_ifs != IP_NULL)
|
|
{
|
|
struct Ipnet_tunnel_ip4_key key;
|
|
|
|
/*
|
|
* Look for interface where both local and remote address of
|
|
* the tunnel is specified
|
|
*/
|
|
key.outer_ip_hdr = outer_ip_hdr;
|
|
key.vr = pkt->vr_index;
|
|
netif = ipcom_hash_get(ipnet->ip4.tunnel_ifs, &key);
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void) netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Look for interface where the only the remote address is
|
|
* specified
|
|
*/
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->dst, IP_INADDR_ANY);
|
|
netif = ipcom_hash_get(ipnet->ip4.tunnel_ifs, &key);
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->dst, dst->flow_spec.to.in.s_addr);
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void)netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Look for interface where the only the local address is
|
|
* specified
|
|
*/
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->src, IP_INADDR_ANY);
|
|
netif = ipcom_hash_get(ipnet->ip4.tunnel_ifs, &key);
|
|
if (netif == IP_NULL)
|
|
{
|
|
/*
|
|
* Look for interface where neither the local nor the
|
|
* remote address is specified
|
|
*/
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->dst, IP_INADDR_ANY);
|
|
netif = ipcom_hash_get(ipnet->ip4.tunnel_ifs, &key);
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->dst, dst->flow_spec.to.in.s_addr);
|
|
}
|
|
|
|
IPNET_IP4_SET_IPADDR(outer_ip_hdr->src, dst->flow_spec.from.in.s_addr);
|
|
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void)netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
if (outer_ip_hdr->p == IP_IPPROTO_IPV6)
|
|
{
|
|
#if defined(IPNET_USE_SIT) || defined(IPNET_USE_6TO4)
|
|
Ipnet_pkt_ip6 *ip6;
|
|
Ipnet_ip6_addr_entry *addr;
|
|
|
|
if (ipcom_pkt_get_length(pkt) < IPNET_IP6_HDR_SIZE)
|
|
{
|
|
/* Cannot contain a valid inner IPv6 header */
|
|
ipcom_pkt_free(pkt);
|
|
return;
|
|
}
|
|
|
|
ip6 = ipcom_pkt_get_data(pkt, 0);
|
|
/*
|
|
* Try to find a tunnel that matches the destination address
|
|
* of the inner IPv6 header
|
|
*/
|
|
addr = ipnet_ip6_get_addr_entry(&ip6->dst, pkt->vr_index, IP_NULL);
|
|
if (addr)
|
|
netif = addr->netif;
|
|
#endif /* IPNET_USE_SIT || IPNET_USE_6TO4 */
|
|
|
|
#ifdef IPNET_USE_SIT
|
|
if (netif != IP_NULL
|
|
&& netif->ipcom.type == IP_IFT_TUNNEL
|
|
&& netif->ipcom.arphrd_type == IP_ARPHRD_SIT)
|
|
{
|
|
(void) netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
#ifdef IPNET_USE_6OVER4
|
|
{
|
|
int i;
|
|
for (i = 0; i < ipnet->netif_6over4_count; i++)
|
|
{
|
|
netif = ipnet->netif_6over4[i];
|
|
if (netif->ipcom.link_index == pkt->ifindex)
|
|
{
|
|
(void)netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif /* IPNET_USE_6OVER4 */
|
|
|
|
#ifdef IPNET_USE_6TO4
|
|
/* RFC 3056, chapter 5.3
|
|
...
|
|
A simple decapsulation rule for incoming IPv4 packets with protocol
|
|
type 41 MUST be implemented:
|
|
|
|
ADDITIONAL DECAPSULATION RULE for 6to4 routers
|
|
|
|
apply any security checks (see Section 8);
|
|
remove the IPv4 header;
|
|
submit the packet to local IPv6 routing.
|
|
...
|
|
*/
|
|
if (netif == IP_NULL)
|
|
{
|
|
Ipnet_route_entry *rt;
|
|
Ipnet_flow_spec flow_spec;
|
|
|
|
ipnet_ip6_flow_spec_from_pkt(&flow_spec,
|
|
pkt,
|
|
ip6,
|
|
IP_TRUE);
|
|
|
|
/*
|
|
* May be a packet that is comming from the 6to4 network
|
|
* through an IPv4 tunnel. This is why the lookup is made
|
|
* on source address, the source will match 2002::/16 if
|
|
* sent from a 6to4 network. The 2002::/16 network will
|
|
* use a 6to4 tunnel interface if such tunnel is currently
|
|
* in use.
|
|
*/
|
|
if (ipnet_route_lookup_ecmp(IP_AF_INET6,
|
|
dst->flow_spec.vr,
|
|
0,
|
|
&ip6->src,
|
|
0,
|
|
0,
|
|
IP_NULL,
|
|
&flow_spec,
|
|
&rt) >= 0
|
|
&& (ipnet_tunnel_get_type(rt->netif->ipcom.name)
|
|
== IP_ARPHRD_TUNNEL6TO4))
|
|
{
|
|
netif = rt->netif;
|
|
}
|
|
}
|
|
|
|
if (netif)
|
|
{
|
|
(void)ipnet_tunnel_rx(netif, pkt);
|
|
return;
|
|
}
|
|
#endif /* IPNET_USE_6TO4 */
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
ipcom_pkt_free(pkt);
|
|
}
|
|
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_rx
|
|
*===========================================================================
|
|
* Description: Process packets tunneled over IPv6 (IPIP, IPV6, GRE).
|
|
* Parameters: dst - destination cache entry for the outer header.
|
|
* pkt - packet data.
|
|
* ip_proto - the value of the 'nxt' field of the header
|
|
* preceding the inner IP header.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_ip6_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt, Ip_u8 ip_proto)
|
|
{
|
|
if (IP_IN6_IS_ADDR_MULTICAST(&dst->flow_spec.to.in6))
|
|
/*
|
|
* Set the link multicast since the outer IPv6 header is the link
|
|
* layer for the inner IP header
|
|
*/
|
|
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_LINK_MULTICAST);
|
|
IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV6);
|
|
|
|
IPCOM_PKT_RESET_CHECKSUM(pkt);
|
|
|
|
if (ipnet->ip6.tunnel_ifs != IP_NULL)
|
|
{
|
|
struct Ipnet_tunnel_ip6_key key;
|
|
Ipnet_netif *netif;
|
|
Ipnet_pkt_ip6 *outer_ip_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
key.outer_ip_hdr = outer_ip_hdr;
|
|
key.ip_proto = ip_proto;
|
|
key.vr = pkt->vr_index;
|
|
netif = ipcom_hash_get(ipnet->ip6.tunnel_ifs, &key);
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void) netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->dst, &ip_in6addr_any);
|
|
netif = ipcom_hash_get(ipnet->ip6.tunnel_ifs, &key);
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->dst, &dst->flow_spec.to.in6);
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void) netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->src, &ip_in6addr_any);
|
|
netif = ipcom_hash_get(ipnet->ip6.tunnel_ifs, &key);
|
|
if (netif == IP_NULL)
|
|
{
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->dst, &ip_in6addr_any);
|
|
netif = ipcom_hash_get(ipnet->ip6.tunnel_ifs, &key);
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->dst, &dst->flow_spec.to.in6);
|
|
}
|
|
IPNET_IP6_SET_ADDR(&outer_ip_hdr->src, &dst->flow_spec.from.in6);
|
|
if (netif != IP_NULL)
|
|
{
|
|
(void) netif->link_input(netif, pkt);
|
|
return;
|
|
}
|
|
|
|
}
|
|
/* else: No tunnel interface defined */
|
|
|
|
ipcom_pkt_free(pkt);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_ipip_rx
|
|
*===========================================================================
|
|
* Description: Reception handler for IPv4-in-IPv6 tunnels
|
|
* Parameters: dst - destination cache entry for the outer header.
|
|
* pkt - packet data.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_ip6_ipip_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
ipnet_tunnel_ip6_rx(dst, pkt, IP_IPPROTO_IPIP);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_ipv6_rx
|
|
*===========================================================================
|
|
* Description: Reception handler for IPv6-in-IPv6 tunnels
|
|
* Parameters: dst - destination cache entry for the outer header.
|
|
* pkt - packet data.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_tunnel_ip6_ipv6_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
/*
|
|
* The protocol is not a typo, all IP-in-IP tunnels uses
|
|
* IP_IPPROTO_IPIP as protocol identifier.
|
|
*/
|
|
ipnet_tunnel_ip6_rx(dst, pkt, IP_IPPROTO_IPIP);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip6_gre_rx
|
|
*===========================================================================
|
|
* Description: Reception handler for GRE tunnels
|
|
* Parameters: dst - destination cache entry for the outer header.
|
|
* pkt - packet data.
|
|
* Returns:
|
|
*
|
|
*/
|
|
#ifdef IPNET_USE_GRE
|
|
IP_STATIC void
|
|
ipnet_tunnel_ip6_gre_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
ipnet_tunnel_ip6_rx(dst, pkt, IP_IPPROTO_GRE);
|
|
}
|
|
#endif
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_get_type
|
|
*===========================================================================
|
|
* Description: Returns the type of tunnel the interface implements.
|
|
* Parameters: name - A tunnel interface name.
|
|
* Returns: One of the IPNET_TUNNEL_TYPE_xxx constants.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u16
|
|
ipnet_tunnel_get_type(char *name)
|
|
{
|
|
if (ipcom_strncmp(name, "gif", 3) == 0)
|
|
return IP_ARPHRD_TUNNEL;
|
|
#ifdef IPNET_USE_GRE
|
|
if (ipcom_strncmp(name, "gre", 3) == 0)
|
|
return IP_ARPHRD_IPGRE;
|
|
#endif
|
|
#ifdef IPNET_USE_SIT
|
|
if (ipcom_strncmp(name, "sit", 3) == 0)
|
|
return IP_ARPHRD_SIT;
|
|
#endif
|
|
#ifdef IPNET_USE_6TO4
|
|
if (ipcom_strncmp(name, "6to4", 4) == 0)
|
|
return IP_ARPHRD_TUNNEL6TO4;
|
|
#endif
|
|
#ifdef IPNET_USE_6OVER4
|
|
if (ipcom_strncmp(name, "6over4", 6) == 0)
|
|
return IP_ARPHRD_TUNNEL6OVER4;
|
|
#endif
|
|
return IP_ARPHRD_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_get_type
|
|
*===========================================================================
|
|
* Description: Returns the type of tunnel the interface implements.
|
|
* Parameters: name - A tunnel interface name.
|
|
* Returns: One of the IPNET_TUNNEL_TYPE_xxx constants.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u16
|
|
ipnet_tunnel_get_basetype(int vr, char *name, Ip_size_t *sz)
|
|
{
|
|
Ipnet_netif *netif = ipnet_if_nametonetif(vr, name);
|
|
Ip_size_t tsz;
|
|
|
|
/* Convenience */
|
|
if (!sz)
|
|
sz = &tsz;
|
|
|
|
*sz = 0;
|
|
|
|
/**/
|
|
if (netif == IP_NULL)
|
|
{
|
|
if (ipcom_strcmp(name, "tunl0") == 0)
|
|
{
|
|
*sz = sizeof(struct Ip_ip_tunnel_parm);
|
|
return IP_ARPHRD_TUNNEL;
|
|
}
|
|
#ifdef IPCOM_USE_INET
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
else if (ipcom_strcmp(name, "gre0") == 0)
|
|
{
|
|
*sz = sizeof(struct Ip_ip_tunnel_parm);
|
|
return IP_ARPHRD_IPGRE;
|
|
}
|
|
#endif /* IPNET_USE_GRE */
|
|
#ifdef IPNET_USE_SIT
|
|
else if (ipcom_strcmp(name, "sit0") == 0)
|
|
{
|
|
*sz = sizeof(struct Ip_ip_tunnel_parm);
|
|
return IP_ARPHRD_SIT;
|
|
}
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
else if (ipcom_strcmp(name, "ip6tnl0") == 0)
|
|
{
|
|
*sz = sizeof(struct Ip_ip6_tnl_parm);
|
|
return IP_ARPHRD_TUNNEL6;
|
|
}
|
|
#ifdef IPNET_USE_GRE
|
|
else if (ipcom_strcmp(name, "ip6gre0") == 0)
|
|
{
|
|
*sz = sizeof(struct Ip_ip6_tnl_parm);
|
|
return IP_ARPHRD_IPGRE6;
|
|
}
|
|
#endif /* IPNET_USE_GRE */
|
|
|
|
#endif /* IPCOM_USE_INET6 */
|
|
return IP_ARPHRD_NONE;
|
|
}
|
|
|
|
return netif->ipcom.arphrd_type;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_get_type
|
|
*===========================================================================
|
|
* Description: Returns the type of tunnel the interface implements.
|
|
* Parameters: name - A tunnel interface name.
|
|
* Returns: One of the IPNET_TUNNEL_TYPE_xxx constants.
|
|
*
|
|
*/
|
|
IP_GLOBAL char *
|
|
ipnet_tunnel_get_basename(int vr, Ip_u16 basetype, char *name)
|
|
{
|
|
Ipnet_netif *netif;
|
|
const char *tbase;
|
|
int i;
|
|
|
|
switch (basetype)
|
|
{
|
|
case IP_ARPHRD_TUNNEL:
|
|
tbase = "tunl";
|
|
break;
|
|
case IP_ARPHRD_TUNNEL6:
|
|
tbase = "ip6tnl";
|
|
break;
|
|
case IP_ARPHRD_IPGRE6:
|
|
tbase = "ip6gre";
|
|
break;
|
|
case IP_ARPHRD_SIT:
|
|
tbase = "sit";
|
|
break;
|
|
case IP_ARPHRD_IPGRE:
|
|
tbase = "gre";
|
|
break;
|
|
default:
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* Find an empty slot */
|
|
for (i = 0; i < 255; i++)
|
|
{
|
|
ipcom_snprintf(name, IP_IFNAMSIZ, "%s%d", tbase, i);
|
|
netif = (Ipnet_netif*)ipcom_if_nametonetif(vr, name);
|
|
|
|
if (netif == IP_NULL)
|
|
return name;
|
|
else
|
|
IPNET_IF_UNLOCK(netif);
|
|
}
|
|
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_type
|
|
*===========================================================================
|
|
* Description: Returns the type of tunnel the interface implements.
|
|
* Parameters: netif - A tunnel instance
|
|
* Returns: One of the IPNET_TUNNEL_TYPE_xxx constants.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_u16
|
|
ipnet_tunnel_type(Ipnet_netif *netif)
|
|
{
|
|
if (netif->ipcom.type == IP_IFT_TUNNEL)
|
|
return netif->ipcom.arphrd_type;
|
|
|
|
return IP_ARPHRD_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_rfc3519_rx
|
|
*===========================================================================
|
|
* Description: Reception handler for RFC3519 (mobile IP traversal) tunnels
|
|
* Parameters: dst - destination cache for the outer header
|
|
* pkt - packet buffer.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_tunnel_ip4_rfc3519_rx(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
Ipnet_pkt_ip *outer_ip_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
Ip_u8 proto;
|
|
|
|
/* Skip the UDP header */
|
|
pkt->start += IPNET_UDP_HDR_SIZE;
|
|
|
|
/* Switch the outer protocol to the 'tunneling protocol' */
|
|
proto = outer_ip_hdr->p = pkt->data[pkt->start + 1];
|
|
|
|
/* Skip the RFC3519 UDP payload */
|
|
pkt->start += 4;
|
|
|
|
/* Go through the ordinary tunnel input */
|
|
ipnet_ip4_transport_rx(dst, pkt, proto);
|
|
}
|
|
|
|
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_ip4_rfc3519_input
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_tunnel_dsmipv6_input(Ipnet_dst_cache *dst, Ipcom_pkt *pkt)
|
|
{
|
|
Ip_u8 proto;
|
|
|
|
/* Skip the UDP header */
|
|
pkt->start += IPNET_UDP_HDR_SIZE;
|
|
|
|
/* Is this a TLV HEADER? */
|
|
if ((pkt->data[pkt->start] & 0xf0) == 0x40)
|
|
proto = IP_IPPROTO_IPIP;
|
|
else if ((pkt->data[pkt->start] & 0xf0) == 0x60)
|
|
proto = IP_IPPROTO_IPV6;
|
|
else
|
|
{
|
|
Ip_u8 *tlv_hdr = ipcom_pkt_get_data(pkt, 0);
|
|
|
|
/* TLV - Set the outer type; don't bother with the length field */
|
|
proto = tlv_hdr[1];
|
|
|
|
/* Skip the DSMIPv6 TLV Header */
|
|
pkt->start += 4;
|
|
}
|
|
|
|
#ifdef IPCOM_USE_INET
|
|
if ((pkt->data[pkt->ipstart] & 0xf0) == 0x40)
|
|
{
|
|
Ipnet_pkt_ip *outer_ip_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
/* Set the protocol in the ip header */
|
|
outer_ip_hdr->p = proto;
|
|
|
|
/* Go through the registered protocol handler */
|
|
ipnet_ip4_transport_rx(dst, pkt, proto);
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef IPCOM_USE_INET6
|
|
if ((pkt->data[pkt->ipstart] & 0xf0) == 0x60)
|
|
{
|
|
Ipnet_pkt_ip6 *outer_ip_hdr = ipcom_pkt_get_iphdr(pkt);
|
|
|
|
/* Set the protocol in the ip header */
|
|
outer_ip_hdr->nxt = proto;
|
|
/* Go through the registered protocol handler */
|
|
ipnet_ip6_transport_rx(dst, pkt, proto);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
ipcom_pkt_free(pkt);
|
|
return -IP_ERRNO_ENXIO;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_setup
|
|
*===========================================================================
|
|
* Description: Initiates a new tunnel interface descriptor in IPNET.
|
|
* Parameters: netif - The new tunnel interface
|
|
* type - Tunnel interface type.
|
|
* Returns: Pointer to interface descriptor.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_tunnel_setup(Ipnet_netif *netif, int type)
|
|
{
|
|
int ret = 0;
|
|
Ipnet_tunnel_plink *plink = netif->ipcom.plink;
|
|
struct Ip_ip_tunnel_param *if_tp = &plink->tp;
|
|
|
|
/* Store the type */
|
|
netif->ipcom.arphrd_type = type;
|
|
|
|
ipnet_if_init_ppp_peer(netif, &plink->peer);
|
|
|
|
switch (type)
|
|
{
|
|
case IP_ARPHRD_TUNNEL:
|
|
case IP_ARPHRD_TUNNEL6:
|
|
if_tp->protocol = IP_IPPROTO_IPIP;
|
|
break;
|
|
|
|
#ifdef IPNET_USE_GRE
|
|
case IP_ARPHRD_IPGRE:
|
|
case IP_ARPHRD_IPGRE6:
|
|
if_tp->protocol = IP_IPPROTO_GRE;
|
|
ret = ipnet_gre_setup(netif);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef IPNET_USE_SIT
|
|
case IP_ARPHRD_SIT:
|
|
if_tp->protocol = IP_IPPROTO_IPV6;
|
|
IP_BIT_CLR(netif->ipcom.flags, IP_IFF_POINTOPOINT | IP_IFF_MULTICAST);
|
|
IP_BIT_SET(netif->flags2, IPNET_IFF2_NO_IPV4_SUPPORT);
|
|
netif->link_output = ipnet_tunnel_sit_tx;
|
|
netif->link_ip6_create_addr = IP_NULL;
|
|
break;
|
|
#endif /* IPNET_USE_SIT */
|
|
|
|
#ifdef IPNET_USE_6TO4
|
|
case IP_ARPHRD_TUNNEL6TO4:
|
|
if_tp->protocol = IP_IPPROTO_IPV6;
|
|
IP_BIT_CLR(netif->ipcom.flags, IP_IFF_POINTOPOINT | IP_IFF_MULTICAST);
|
|
netif->link_output = ipnet_tunnel_6to4_tx;
|
|
netif->link_ip6_create_addr = IP_NULL;
|
|
break;
|
|
#endif /* IPNET_USE_6TO4 */
|
|
|
|
#ifdef IPNET_USE_6OVER4
|
|
case IP_ARPHRD_TUNNEL6OVER4:
|
|
if_tp->protocol = IP_IPPROTO_IPV6;
|
|
IP_BIT_CLR(netif->ipcom.flags, IP_IFF_POINTOPOINT | IP_IFF_NOARP);
|
|
IP_BIT_SET(netif->flags2, IPNET_IFF2_NO_IPV4_SUPPORT);
|
|
netif->link_output = ipnet_tunnel_6over4_tx;
|
|
netif->link_ip6_create_addr = ipnet_tunnel_6over4_create_addr;
|
|
netif->neigh_create = ipnet_netif_neigh_create;
|
|
break;
|
|
#endif /* IPNET_USE_6TO4 */
|
|
|
|
default:
|
|
ret = -IP_ERRNO_EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_add
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_tunnel_add(Ipnet_socket *sock, const char *name, Ip_u16 type, Ipnet_netif **netifp)
|
|
{
|
|
Ipnet_netif *netif;
|
|
int ret;
|
|
int ifindex = 0;
|
|
|
|
if (netifp)
|
|
*netifp = IP_NULL;
|
|
|
|
netif = (Ipnet_netif *)ipcom_if_malloc(IP_IFT_TUNNEL);
|
|
if (netif == IP_NULL)
|
|
{
|
|
ret = -IP_ERRNO_ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
/* Set the interface name */
|
|
ipcom_strncpy(netif->ipcom.name, name, IP_IFNAMSIZ);
|
|
netif->ipcom.name[IP_IFNAMSIZ-1] = '\0';
|
|
netif->vr_index = sock->vr_index;
|
|
|
|
ret = ipnet_tunnel_setup(netif, type);
|
|
if (ret < 0)
|
|
goto badout;
|
|
|
|
ret = ipcom_if_attach(&netif->ipcom);
|
|
if (ret < 0)
|
|
goto badout;
|
|
ifindex = ret;
|
|
|
|
if (netifp)
|
|
*netifp = netif;
|
|
|
|
if (type == IP_ARPHRD_SIT || type == IP_ARPHRD_TUNNEL6TO4)
|
|
{
|
|
/*
|
|
* SIT/6to4 interfaces is always running since the source and
|
|
* destination addresses are extracted from the inner IPv6
|
|
* packet
|
|
*/
|
|
ipnet_kioevent(netif, IP_EIOXRUNNING, IP_NULL, 0);
|
|
}
|
|
|
|
done:
|
|
if (ret < 0)
|
|
return ret;
|
|
return ifindex;
|
|
|
|
badout:
|
|
(void)ipcom_if_free(&netif->ipcom);
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_create_bsd
|
|
*===========================================================================
|
|
* Description: Creates and attaches a new tunnel interface to IPNET.
|
|
* Parameters: ifreq - The name of the new tunnel interface
|
|
* or will contain the name of the new interface if ifreq->ifr_name
|
|
* is a zero length string.
|
|
* Returns: >0 = the interface index , 0< = error code.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_tunnel_create_bsd(Ipnet_socket *sock, struct Ip_ifreq *ifreq)
|
|
{
|
|
Ip_u16 type = ipnet_tunnel_get_type(ifreq->ifr_name);
|
|
|
|
if (type == IP_ARPHRD_NONE)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
return ipnet_tunnel_add(sock, ifreq->ifr_name, type, IP_NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_create_bsd
|
|
*===========================================================================
|
|
* Description: Creates and attaches a new tunnel interface to IPNET.
|
|
* Parameters: ifreq - The name of the new tunnel interface
|
|
* or will contain the name of the new interface if ifreq->ifr_name
|
|
* is a zero length string.
|
|
* Returns: >0 = the interface index , 0< = error code.
|
|
*
|
|
*/
|
|
IP_GLOBAL int
|
|
ipnet_tunnel_create_linux(Ipnet_socket *sock, struct Ip_ifreq *ifreq)
|
|
{
|
|
Ip_u16 basetype = ipnet_tunnel_get_basetype(sock->vr_index, ifreq->ifr_name, IP_NULL);
|
|
int ret;
|
|
char *name;
|
|
Ipnet_netif *netif;
|
|
|
|
if (basetype == IP_ARPHRD_NONE)
|
|
return -IP_ERRNO_EINVAL;
|
|
|
|
name = (char *)ifreq->ip_ifr_data;
|
|
if (*name == 0)
|
|
{
|
|
if (!ipnet_tunnel_get_basename(sock->vr_index, basetype, name))
|
|
return -IP_ERRNO_EINVAL;
|
|
}
|
|
|
|
/* First element is name */
|
|
if ((ret = ipnet_tunnel_add(sock, name, basetype, &netif)) >= 0)
|
|
ret = ipnet_tunnel_ioctl(netif, IP_X_SIOCCHGTUNNEL, ifreq);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_init
|
|
*===========================================================================
|
|
* Description: Register the supported tunnel types into the IP layer.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipnet_tunnel_init(void)
|
|
{
|
|
/*
|
|
* The transport layer for the outer header determines the tunnel
|
|
* type.
|
|
*/
|
|
#ifdef IPCOM_USE_INET
|
|
/*
|
|
* All tunnel types that run over IPv4
|
|
*/
|
|
ipnet_ip4_reg_transport_layer(IP_IPPROTO_IPIP, ipnet_tunnel_ip4_rx);
|
|
ipnet_ip4_reg_transport_layer(IP_IPPROTO_MOBILE, ipnet_tunnel_ip4_rx);
|
|
#ifdef IPCOM_USE_INET6
|
|
ipnet_ip4_reg_transport_layer(IP_IPPROTO_IPV6, ipnet_tunnel_ip4_rx);
|
|
#endif
|
|
#ifdef IPNET_USE_GRE
|
|
ipnet_ip4_reg_transport_layer(IP_IPPROTO_GRE, ipnet_tunnel_ip4_rx);
|
|
#endif
|
|
#endif /* IPCOM_USE_INET */
|
|
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
/*
|
|
* All tunnel types that run over IPv6
|
|
*/
|
|
ipnet_ip6_reg_transport_layer(IP_IPPROTO_IPV6, ipnet_tunnel_ip6_ipv6_rx);
|
|
#ifdef IPCOM_USE_INET
|
|
ipnet_ip6_reg_transport_layer(IP_IPPROTO_IPIP, ipnet_tunnel_ip6_ipip_rx);
|
|
#endif
|
|
#ifdef IPNET_USE_GRE
|
|
ipnet_ip6_reg_transport_layer(IP_IPPROTO_GRE, ipnet_tunnel_ip6_gre_rx);
|
|
#endif
|
|
#endif /* IPCOM_USE_INET */
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_tunnel_if_init
|
|
*===========================================================================
|
|
* Description: Initializes a new tunnel interface.
|
|
* Parameters: [in] netif - The tunnel interface to initialize.
|
|
* Returns: 0 = success, 0< = error code.
|
|
*
|
|
*/
|
|
IP_PUBLIC int
|
|
ipnet_tunnel_if_init(Ipnet_netif *netif)
|
|
{
|
|
ip_assert(netif->ipcom.type == IP_IFT_TUNNEL);
|
|
|
|
netif->ipcom.plink = ipcom_calloc(1, sizeof(Ipnet_tunnel_plink));
|
|
if (netif->ipcom.plink == IP_NULL)
|
|
return -IP_ERRNO_ENOMEM;
|
|
|
|
/* Init tunnel interface */
|
|
/* Correct MTU will be calculated in ioctl(IP_SIOCCHGTUNNEL) */
|
|
netif->ipcom.arphrd_type = IP_ARPHRD_NONE;
|
|
netif->ipcom.mtu = 1280;
|
|
netif->link_ioctl = ipnet_tunnel_ioctl;
|
|
netif->ipcom.flags = IP_IFF_POINTOPOINT | IP_IFF_NOARP | IP_IFF_MULTICAST | IP_IFF_SIMPLEX;
|
|
netif->link_input = ipnet_tunnel_rx;
|
|
netif->link_output = ipnet_tunnel_tx;
|
|
netif->link_get_frame_info = ipnet_tunnel_get_frame_info;
|
|
netif->neigh_create = ipnet_tunnel_neigh_create;
|
|
#ifdef IPCOM_USE_PACKET
|
|
netif->link_raw_output = IP_NULL;
|
|
#endif /* IPCOM_USE_PACKET */
|
|
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
netif->link_ip6_create_addr = ipnet_tunnel_ip6_create_addr;
|
|
netif->inet6_if_id_bits = 64;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* IPNET_USE_TUNNEL */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|