blob: 242d6ac4004611a95648b0f7515e0f268c1134ec [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
/*
* nss_tunipip6.c
*
* This file is the NSS DS-lit and IPP6 tunnel module
* ------------------------REVISION HISTORY-----------------------------
* Qualcomm Atheros 15/sep/2013 Created
*/
#include <linux/types.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <net/ipv6.h>
#include <net/ipip.h>
#include <net/ip6_tunnel.h>
#include <linux/if_arp.h>
#include "nss_api_if.h"
#include "nss_hlos_if.h"
#define NSS_TUNIPIP6_IF_NUMBER 11
/*
* NSS tunipip6 debug macros
*/
#if (NSS_TUNIPIP6_DEBUG_LEVEL < 1)
#define nss_tunipip6_assert(fmt, args...)
#else
#define nss_tunipip6_assert(c) if (!(c)) { BUG_ON(!(c)); }
#endif
#if (NSS_TUNIPIP6_DEBUG_LEVEL < 2)
#define nss_tunipip6_error(fmt, args...)
#else
#define nss_tunipip6_error(fmt, args...) printk(KERN_WARNING "nss tunipip6:"fmt, ##args)
#endif
#if (NSS_TUNIPIP6_DEBUG_LEVEL < 3)
#define nss_tunipip6_warning(fmt, args...)
#else
#define nss_tunipip6_warning(fmt, args...) printk(KERN_WARNING "nss tunipip6:"fmt, ##args)
#endif
#if (NSS_TUNIPIP6_DEBUG_LEVEL < 4)
#define nss_tunipip6_info(fmt, args...)
#else
#define nss_tunipip6_info(fmt, args...) printk(KERN_INFO "nss tunipip6 :"fmt, ##args)
#endif
#if (NSS_TUNIPIP6_DEBUG_LEVEL < 5)
#define nss_tunipip6_trace(fmt, args...)
#else
#define nss_tunipip6_trace(fmt, args...) printk(KERN_DEBUG "nss tunipip6 :"fmt, ##args)
#endif
void nss_tunipip6_exception(void *ctx, void *buf);
enum tunipip6_metadata_types {
TUNIPIP6_METADATA_TYPE_IF_UP,
TUNIPIP6_METADATA_TYPE_IF_DOWN
};
/*
* DS-lite and ipip6 configuration command structure
*/
struct nss_tunnel_ipip6_cfg{
uint32_t saddr[4]; /* Tunnel source address */
uint32_t daddr[4]; /* Tunnel destination address */
uint32_t flowlabel; /* Tunnel ipv6 flowlabel */
uint32_t flags; /* Tunnel additional flags */
uint8_t hop_limit; /* Tunnel ipv6 hop limit */
};
/*
* DS-lite and ipip6 interface down command structure
*/
struct tunipip6_if_down_param{
uint32_t dummy[4]; /* dummy parameter */
};
/*
* DS-lite and ipip6 tunnel generic param
*/
struct nss_tunnel_ipip6_param {
enum tunipip6_metadata_types type;
union {
struct nss_tunnel_ipip6_cfg cfg;
struct tunipip6_if_down_param ifdown_param;
}sub;
};
/*
* nss_tunipip6_tunnel
* DS-lite and ipip 6tunnel host instance
*/
struct nss_tunipip6_tunnel{
void *nss_ctx;
uint32_t if_num;
struct net_device *netdev;
uint32_t device_up;
};
struct nss_tunipip6_tunnel g_tunipip6;
/*
* Internal function
*/
static int
nss_tunipip6_dev_event(struct notifier_block *nb,
unsigned long event,
void *dev);
/*
* Linux Net device Notifier
*/
struct notifier_block nss_tunipip6_notifier = {
.notifier_call = nss_tunipip6_dev_event,
};
/*
* nss_tunipip6_dev_up()
* IPIP6 Tunnel device i/f up handler
*/
void nss_tunipip6_dev_up( struct net_device * netdev)
{
struct ip6_tnl *tunnel;
struct nss_tunnel_ipip6_param tnlparam;
struct nss_tunnel_ipip6_cfg *tnlcfg;
struct flowi6 *fl6;
nss_tx_status_t status;
/*
* Validate netdev for ipv6-in-ipv4 Tunnel
*/
if (netdev->type != ARPHRD_TUNNEL6 ) {
return;
}
tunnel = (struct ip6_tnl *)netdev_priv(netdev);
/*
* Find he Tunnel device flow information
*/
fl6 = &tunnel->fl.u.ip6;
nss_tunipip6_trace(" Tunnel Param srcaddr %x:%x:%x:%x daddr %x:%x:%x:%x \n",
fl6->saddr.s6_addr32[0], fl6->saddr.s6_addr32[1],
fl6->saddr.s6_addr32[2], fl6->saddr.s6_addr32[3],
fl6->daddr.s6_addr32[0], fl6->daddr.s6_addr32[1],
fl6->daddr.s6_addr32[2], fl6->daddr.s6_addr32[3] );
nss_tunipip6_trace(" hop limit %d \n", tunnel->parms.hop_limit);
nss_tunipip6_trace(" tunnel param flag %x fl6.flowlabel %x \n", tunnel->parms.flags, fl6->flowlabel);
/*
*Prepare The Tunnel configuration parameter to send to nss
*/
memset( &tnlparam, 0, sizeof(struct nss_tunnel_ipip6_param));
tnlparam.type = TUNIPIP6_METADATA_TYPE_IF_UP;
tnlcfg = (struct nss_tunnel_ipip6_cfg *)&tnlparam.sub.cfg;
tnlcfg->saddr[0] = ntohl(fl6->saddr.s6_addr32[0]);
tnlcfg->saddr[1] = ntohl(fl6->saddr.s6_addr32[1]);
tnlcfg->saddr[2] = ntohl(fl6->saddr.s6_addr32[2]);
tnlcfg->saddr[3] = ntohl(fl6->saddr.s6_addr32[3]);
tnlcfg->daddr[0] = ntohl(fl6->daddr.s6_addr32[0]);
tnlcfg->daddr[1] = ntohl(fl6->daddr.s6_addr32[1]);
tnlcfg->daddr[2] = ntohl(fl6->daddr.s6_addr32[2]);
tnlcfg->daddr[3] = ntohl(fl6->daddr.s6_addr32[3]);
tnlcfg->hop_limit = tunnel->parms.hop_limit;
tnlcfg->flags = ntohl(tunnel->parms.flags);
tnlcfg->flowlabel = fl6->flowlabel; /*flow Label In kernel is stored in big endian format*/
nss_tunipip6_trace(" Tunnel Param srcaddr %x:%x:%x:%x daddr %x:%x:%x:%x \n",
tnlcfg->saddr[0], tnlcfg->saddr[1],
tnlcfg->saddr[2], tnlcfg->saddr[3],
tnlcfg->daddr[0], tnlcfg->daddr[1],
tnlcfg->daddr[2], tnlcfg->daddr[3] );
/*
* Register ipip6 tunnel with NSS
*/
g_tunipip6.nss_ctx = nss_register_tunipip6_if(g_tunipip6.if_num,
nss_tunipip6_exception,
netdev);
if (g_tunipip6.nss_ctx == NULL) {
nss_tunipip6_trace("nss_register_tunipip6_if Failed \n");
return;
} else {
nss_tunipip6_trace("nss_register_tunipip6_if Success \n");
}
nss_tunipip6_trace("Sending IPIP6 tunnel i/f up command to NSS %x \n",
(int)g_tunipip6.nss_ctx);
/*
* Send IPIP6 Tunnel UP command to NSS
*/
status = nss_tx_generic_if_buf(g_tunipip6.nss_ctx,
g_tunipip6.if_num,
(uint8_t *)&tnlparam,
sizeof(struct nss_tunnel_ipip6_param));
if (status != NSS_TX_SUCCESS) {
nss_tunipip6_error("Tunnel up command error %d \n", status);
return;
}
g_tunipip6.device_up = 1;
}
/*
* nss_tunipip6_dev_down()
* IPP6 Tunnel device i/f down handler
*/
void nss_tunipip6_dev_down( struct net_device * netdev)
{
struct nss_tunnel_ipip6_param tnlparam;
nss_tx_status_t status;
/*
* Check if tunnel ipip6 is registered ?
*/
if(g_tunipip6.nss_ctx == NULL){
return;
}
/*
* Validate netdev for ipv6-in-ipv4 Tunnel
*/
if (netdev->type != ARPHRD_TUNNEL6 ) {
return;
}
/*
* TODO: Strick check required if its the same tunnel
* registerd with us
*/
memset( &tnlparam, 0, sizeof(struct nss_tunnel_ipip6_param));
tnlparam.type = TUNIPIP6_METADATA_TYPE_IF_DOWN;
nss_tunipip6_trace("Sending Tunnel ipip6 Down command %x \n",g_tunipip6.if_num);
status = nss_tx_generic_if_buf(g_tunipip6.nss_ctx,
g_tunipip6.if_num,
(uint8_t *)&tnlparam,
sizeof(struct nss_tunnel_ipip6_param));
if (status != NSS_TX_SUCCESS) {
nss_tunipip6_error("Tunnel down command error %d \n", status);
return;
}
/*
* Un-Register IPIP6 tunnel with NSS
*/
nss_unregister_tunipip6_if(g_tunipip6.if_num);
g_tunipip6.nss_ctx = NULL;
g_tunipip6.device_up = 0;
}
/*
* nss_tun6rd_dev_event()
* Net device notifier for ipip6 module
*/
static int nss_tunipip6_dev_event(struct notifier_block *nb,
unsigned long event, void *dev)
{
struct net_device *netdev = (struct net_device *)dev;
nss_tunipip6_trace("%s\n",__FUNCTION__);
switch (event) {
case NETDEV_UP:
nss_tunipip6_trace(" NETDEV_UP :event %lu name %s \n",
event,netdev->name);
nss_tunipip6_dev_up(netdev);
break;
case NETDEV_DOWN:
nss_tunipip6_trace(" NETDEV_DOWN :event %lu name %s \n",
event,netdev->name);
nss_tunipip6_dev_down(netdev);
break;
default:
nss_tunipip6_trace("Unhandled notifier dev %s event %x \n",
netdev->name,(int)event);
break;
}
return NOTIFY_DONE;
}
/*
* nss_tunipip6_exception()
* Exception handler registered to NSS driver
*/
void nss_tunipip6_exception(void *ctx, void *buf)
{
struct net_device *dev = (struct net_device *)ctx;
struct sk_buff *skb = (struct sk_buff *)buf;
const struct iphdr *iph;
skb->dev = dev;
nss_tunipip6_info("received - %d bytes name %s ver %x \n",
skb->len,dev->name,skb->data[0]);
iph = (const struct iphdr *)skb->data;
/*
*Packet after Decap/Encap Did not find the Rule.
*/
if (iph->version == 4) {
skb->protocol = htons(ETH_P_IP);
} else {
skb->protocol = htons(ETH_P_IPV6);
}
skb_reset_network_header(skb);
skb->pkt_type = PACKET_HOST;
skb->skb_iif = dev->ifindex;
skb->ip_summed = CHECKSUM_NONE;
netif_receive_skb(skb);
}
/*
* nss_tunipip6_init_module()
* Tunnel ipip6 module init function
*/
int __init nss_tunipip6_init_module(void)
{
nss_tunipip6_info("module (platform - IPQ806x , Build - %s:%s) loaded\n",
__DATE__, __TIME__);
register_netdevice_notifier(&nss_tunipip6_notifier);
nss_tunipip6_trace("Netdev Notifier registerd \n");
g_tunipip6.if_num = NSS_TUNIPIP6_IF_NUMBER;
g_tunipip6.netdev = NULL;
g_tunipip6.device_up = 0;
g_tunipip6.nss_ctx = NULL;
return 0;
}
/*
* nss_tunipip6_exit_module()
* Tunnel ipip6 module exit function
*/
void __exit nss_tunipip6_exit_module(void)
{
unregister_netdevice_notifier(&nss_tunipip6_notifier);
nss_tunipip6_info("module unloaded\n");
}
module_init(nss_tunipip6_init_module);
module_exit(nss_tunipip6_exit_module);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("NSS tunipip6 offload manager");