Bharath M Kumar | 0d87e91 | 2013-08-12 18:32:57 +0530 | [diff] [blame^] | 1 | /* |
| 2 | ************************************************************************** |
| 3 | * Copyright (c) 2013, Qualcomm Atheros, Inc. |
| 4 | * Permission to use, copy, modify, and/or distribute this software for |
| 5 | * any purpose with or without fee is hereby granted, provided that the |
| 6 | * above copyright notice and this permission notice appear in all copies. |
| 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| 13 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | ************************************************************************** |
| 15 | */ |
| 16 | |
| 17 | /* |
| 18 | * nss_tun6rd.c |
| 19 | * |
| 20 | * This file is the NSS 6rd tunnel module |
| 21 | * ------------------------REVISION HISTORY----------------------------- |
| 22 | * Qualcomm Atheros 15/sep/2013 Created |
| 23 | */ |
| 24 | |
| 25 | #include <linux/types.h> |
| 26 | #include <linux/ip.h> |
| 27 | #include <linux/tcp.h> |
| 28 | #include <linux/module.h> |
| 29 | #include <linux/skbuff.h> |
| 30 | #include <net/ipv6.h> |
| 31 | #include <net/ipip.h> |
| 32 | #include <linux/if_arp.h> |
| 33 | #include "nss_api_if.h" |
| 34 | #include "nss_hlos_if.h" |
| 35 | |
| 36 | #define NSS_TUNRD_IF_NUMBER 10 |
| 37 | |
| 38 | /* |
| 39 | * NSS tun6rd debug macros |
| 40 | */ |
| 41 | #if (NSS_TUN6RD_DEBUG_LEVEL < 1) |
| 42 | #define nss_tun6rd_assert(fmt, args...) |
| 43 | #else |
| 44 | #define nss_tun6d_assert(c) if (!(c)) { BUG_ON(!(c)); } |
| 45 | #endif |
| 46 | |
| 47 | #if (NSS_TUN6RD_DEBUG_LEVEL < 2) |
| 48 | #define nss_tun6rd_error(fmt, args...) |
| 49 | #else |
| 50 | #define nss_tun6rd_error(fmt, args...) printk(KERN_WARNING "nss tun6rd:"fmt, ##args) |
| 51 | #endif |
| 52 | |
| 53 | #if (NSS_TUN6RD_DEBUG_LEVEL < 3) |
| 54 | #define nss_tun6rd_warning(fmt, args...) |
| 55 | #else |
| 56 | #define nss_tun6rd_warning(fmt, args...) printk(KERN_WARNING "nss tun6rd:"fmt, ##args) |
| 57 | #endif |
| 58 | |
| 59 | #if (NSS_TUN6RD_DEBUG_LEVEL < 4) |
| 60 | #define nss_tun6rd_info(fmt, args...) |
| 61 | #else |
| 62 | #define nss_tun6rd_info(fmt, args...) printk(KERN_INFO "nss tun6rd :"fmt, ##args) |
| 63 | #endif |
| 64 | |
| 65 | #if (NSS_TUN6RD_DEBUG_LEVEL < 5) |
| 66 | #define nss_tun6rd_trace(fmt, args...) |
| 67 | #else |
| 68 | #define nss_tun6rd_trace(fmt, args...) printk(KERN_DEBUG "nss tun6rd :"fmt, ##args) |
| 69 | #endif |
| 70 | |
| 71 | void nss_tun6rd_exception(void *ctx, void *buf); |
| 72 | |
| 73 | enum tun6rd_metadata_types { |
| 74 | TUN6RD_METADATA_TYPE_IF_UP, |
| 75 | TUN6RD_METADATA_TYPE_IF_DOWN |
| 76 | }; |
| 77 | |
| 78 | /* |
| 79 | * 6rd configuration command structure |
| 80 | */ |
| 81 | struct nss_tunnel_6rd_cfg{ |
| 82 | uint32_t prefix[4]; /*6rd prefix */ |
| 83 | uint32_t relay_prefix; /* Relay prefix */ |
| 84 | uint16_t prefixlen; /* 6rd prefix len */ |
| 85 | uint16_t relay_prefixlen; /* Relay prefix length*/ |
| 86 | uint32_t saddr; /* Tunnel source address */ |
| 87 | uint32_t daddr; /* Tunnel destination addresss */ |
| 88 | uint8_t tos; /* Tunnel tos field */ |
| 89 | uint8_t ttl; /* Tunnel ttl field */ |
| 90 | |
| 91 | }; |
| 92 | |
| 93 | /* |
| 94 | * 6rd tunnel interface down command structure |
| 95 | */ |
| 96 | struct tun6rd_if_down_param{ |
| 97 | uint32_t prefix[4]; /*Tunnel 6rd prefix */ |
| 98 | }; |
| 99 | |
| 100 | /* |
| 101 | * 6rd Tunnel generic param |
| 102 | */ |
| 103 | struct nss_tunnel_6rd_param { |
| 104 | enum tun6rd_metadata_types type; |
| 105 | union { |
| 106 | struct nss_tunnel_6rd_cfg cfg; |
| 107 | struct tun6rd_if_down_param ifdown_param; |
| 108 | }sub; |
| 109 | }; |
| 110 | |
| 111 | /* |
| 112 | * 6rd tunnel host instance |
| 113 | */ |
| 114 | struct nss_tun6rd_tunnel{ |
| 115 | void *nss_ctx; |
| 116 | uint32_t if_num; |
| 117 | struct net_device *netdev; |
| 118 | uint32_t device_up; |
| 119 | }; |
| 120 | |
| 121 | struct nss_tun6rd_tunnel g_tun6rd; |
| 122 | |
| 123 | /* |
| 124 | * Internal function |
| 125 | */ |
| 126 | static int |
| 127 | nss_tun6rd_dev_event(struct notifier_block *nb, |
| 128 | unsigned long event, |
| 129 | void *dev); |
| 130 | |
| 131 | /* |
| 132 | * Linux Net device Notifier |
| 133 | */ |
| 134 | struct notifier_block nss_tun6rd_notifier = { |
| 135 | .notifier_call = nss_tun6rd_dev_event, |
| 136 | }; |
| 137 | |
| 138 | /* |
| 139 | * nss_tun6rd_dev_up() |
| 140 | * 6RD Tunnel device i/f up handler |
| 141 | */ |
| 142 | void nss_tun6rd_dev_up( struct net_device * netdev) |
| 143 | { |
| 144 | struct ip_tunnel *tunnel; |
| 145 | struct ip_tunnel_6rd_parm *ip6rd; |
| 146 | const struct iphdr *tiph; |
| 147 | struct nss_tunnel_6rd_param tun6rdparam; |
| 148 | struct nss_tunnel_6rd_cfg *tun6rdcfg; |
| 149 | nss_tx_status_t status; |
| 150 | |
| 151 | /* |
| 152 | * Validate netdev for ipv6-in-ipv4 Tunnel |
| 153 | */ |
| 154 | if (netdev->type != ARPHRD_SIT ) { |
| 155 | return; |
| 156 | } |
| 157 | |
| 158 | tunnel = (struct ip_tunnel*)netdev_priv(netdev); |
| 159 | ip6rd = &tunnel->ip6rd; |
| 160 | |
| 161 | /* |
| 162 | * Valid 6rd Tunnel Check |
| 163 | * 1. 6rd Prefix len should be non zero |
| 164 | * 2. Relay prefix length should not be greater then 32 |
| 165 | * 3. To allow for stateless address auto-configuration on the CE LAN side, |
| 166 | * 6rd delegated prefix SHOULD be /64 or shorter. |
| 167 | */ |
| 168 | if ((ip6rd->prefixlen == 0 ) |
| 169 | || (ip6rd->relay_prefixlen > 32) |
| 170 | || (ip6rd->prefixlen |
| 171 | + (32 - ip6rd->relay_prefixlen) > 64)){ |
| 172 | |
| 173 | nss_tun6rd_error("Invalid 6rd argument prefix len %d \ |
| 174 | relayprefix len %d \n", |
| 175 | ip6rd->prefixlen,ip6rd->relay_prefixlen); |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | nss_tun6rd_info(" Valid 6rd Tunnel Prefix %x %x %x %x \n \ |
| 180 | prefix len %d relay_prefix %d relay_prefixlen %d \n", |
| 181 | ip6rd->prefix.s6_addr32[0],ip6rd->prefix.s6_addr32[1], |
| 182 | ip6rd->prefix.s6_addr32[2],ip6rd->prefix.s6_addr32[3], |
| 183 | ip6rd->prefixlen, ip6rd->relay_prefix, |
| 184 | ip6rd->relay_prefixlen); |
| 185 | |
| 186 | /* |
| 187 | * Prepare The Tunnel configuration parameter to send to nss |
| 188 | */ |
| 189 | memset( &tun6rdparam, 0, sizeof(struct nss_tunnel_6rd_param)); |
| 190 | tun6rdparam.type = TUN6RD_METADATA_TYPE_IF_UP; |
| 191 | tun6rdcfg = (struct nss_tunnel_6rd_cfg *)&tun6rdparam.sub.cfg; |
| 192 | |
| 193 | /* |
| 194 | * Find the Tunnel device ipHeader info |
| 195 | */ |
| 196 | tiph = &tunnel->parms.iph ; |
| 197 | nss_tun6rd_trace(" Tunnel Param srcaddr %x daddr %x ttl %d tos %x\n", |
| 198 | tiph->saddr, tiph->daddr,tiph->ttl,tiph->tos); |
| 199 | |
| 200 | if(tiph->saddr == 0) { |
| 201 | nss_tun6rd_error("Tunnel src address not configured %x\n", |
| 202 | tiph->saddr); |
| 203 | return; |
| 204 | } |
| 205 | |
| 206 | if (tiph->daddr == 0) { |
| 207 | nss_tun6rd_error("Tunnel dest address not configured %x\n", |
| 208 | tiph->daddr); |
| 209 | return; |
| 210 | } |
| 211 | |
| 212 | tun6rdcfg->prefixlen = ip6rd->prefixlen; |
| 213 | tun6rdcfg->relay_prefix = ip6rd->relay_prefix; |
| 214 | tun6rdcfg->relay_prefixlen = ip6rd->relay_prefixlen; |
| 215 | tun6rdcfg->saddr = ntohl(tiph->saddr); |
| 216 | tun6rdcfg->daddr = ntohl(tiph->daddr); |
| 217 | tun6rdcfg->prefix[0] = ntohl(ip6rd->prefix.s6_addr32[0]); |
| 218 | tun6rdcfg->prefix[1] = ntohl(ip6rd->prefix.s6_addr32[1]); |
| 219 | tun6rdcfg->prefix[2] = ntohl(ip6rd->prefix.s6_addr32[2]); |
| 220 | tun6rdcfg->prefix[3] = ntohl(ip6rd->prefix.s6_addr32[3]); |
| 221 | tun6rdcfg->ttl = tiph->ttl; |
| 222 | tun6rdcfg->tos = tiph->tos; |
| 223 | |
| 224 | nss_tun6rd_trace(" 6rd Tunnel info \n"); |
| 225 | nss_tun6rd_trace(" saddr %x daddr %d ttl %x tos %x \n", |
| 226 | tiph->saddr, tiph->daddr, tiph->ttl, tiph->tos); |
| 227 | nss_tun6rd_trace(" Prefix %x:%x:%x:%x Prefix len %d \n", |
| 228 | ip6rd->prefix.s6_addr32[0], ip6rd->prefix.s6_addr32[1], |
| 229 | ip6rd->prefix.s6_addr32[2], ip6rd->prefix.s6_addr32[3], |
| 230 | ip6rd->prefixlen); |
| 231 | nss_tun6rd_trace("Relay Prefix %x Len %d\n", |
| 232 | ip6rd->relay_prefix, ip6rd->relay_prefixlen); |
| 233 | |
| 234 | /* |
| 235 | * Register 6rd tunnel with NSS |
| 236 | */ |
| 237 | g_tun6rd.nss_ctx = nss_register_tun6rd_if(g_tun6rd.if_num, |
| 238 | nss_tun6rd_exception, |
| 239 | netdev); |
| 240 | if (g_tun6rd.nss_ctx == NULL) { |
| 241 | nss_tun6rd_trace("nss_register_tun6rd_if Failed \n"); |
| 242 | return; |
| 243 | } else { |
| 244 | nss_tun6rd_trace("nss_register_tun6rd_if Success \n"); |
| 245 | } |
| 246 | |
| 247 | nss_tun6rd_trace("Sending 6rd tunnel i/f up command to NSS %x \n", |
| 248 | (int)g_tun6rd.nss_ctx); |
| 249 | |
| 250 | /* |
| 251 | * Send 6rd Tunnel UP command to NSS |
| 252 | */ |
| 253 | status = nss_tx_generic_if_buf(g_tun6rd.nss_ctx, |
| 254 | g_tun6rd.if_num, |
| 255 | (uint8_t *)&tun6rdparam, |
| 256 | sizeof(struct nss_tunnel_6rd_param)); |
| 257 | |
| 258 | if (status != NSS_TX_SUCCESS) { |
| 259 | nss_tun6rd_error("Tunnel up command error %d \n", status); |
| 260 | return; |
| 261 | } |
| 262 | |
| 263 | g_tun6rd.device_up = 1; |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | * nss_tun6rd_dev_down() |
| 268 | * 6RD Tunnel device i/f down handler |
| 269 | */ |
| 270 | void nss_tun6rd_dev_down( struct net_device * netdev) |
| 271 | { |
| 272 | struct ip_tunnel *tunnel; |
| 273 | struct ip_tunnel_6rd_parm *ip6rd; |
| 274 | struct nss_tunnel_6rd_param tun6rdparam; |
| 275 | struct tun6rd_if_down_param *ifdown; |
| 276 | nss_tx_status_t status; |
| 277 | |
| 278 | /* |
| 279 | * Check if tunnel 6rd is registered ? |
| 280 | */ |
| 281 | if (g_tun6rd.nss_ctx == NULL) { |
| 282 | return; |
| 283 | } |
| 284 | |
| 285 | /* |
| 286 | * Validate netdev for ipv6-in-ipv4 Tunnel |
| 287 | */ |
| 288 | if (netdev->type != ARPHRD_SIT ) { |
| 289 | return; |
| 290 | } |
| 291 | |
| 292 | tunnel = (struct ip_tunnel*)netdev_priv(netdev); |
| 293 | ip6rd = &tunnel->ip6rd; |
| 294 | |
| 295 | /* |
| 296 | * Valid 6rd Tunnel Check |
| 297 | */ |
| 298 | if ((ip6rd->prefixlen == 0 ) |
| 299 | || (ip6rd->relay_prefixlen > 32 ) |
| 300 | || (ip6rd->prefixlen |
| 301 | + (32 - ip6rd->relay_prefixlen) > 64)){ |
| 302 | |
| 303 | nss_tun6rd_error("Invalid 6rd argument prefix len %d \ |
| 304 | relayprefix len %d \n", |
| 305 | ip6rd->prefixlen,ip6rd->relay_prefixlen); |
| 306 | return; |
| 307 | } |
| 308 | |
| 309 | memset( &tun6rdparam, 0, sizeof(struct nss_tunnel_6rd_param)); |
| 310 | tun6rdparam.type = TUN6RD_METADATA_TYPE_IF_DOWN; |
| 311 | ifdown = (struct tun6rd_if_down_param *)&tun6rdparam.sub.ifdown_param; |
| 312 | ifdown->prefix[0] = ntohl(ip6rd->prefix.s6_addr32[0]); |
| 313 | ifdown->prefix[1] = ntohl(ip6rd->prefix.s6_addr32[1]); |
| 314 | ifdown->prefix[2] = ntohl(ip6rd->prefix.s6_addr32[2]); |
| 315 | ifdown->prefix[3] = ntohl(ip6rd->prefix.s6_addr32[3]); |
| 316 | |
| 317 | nss_tun6rd_trace(" Prefix %x:%x:%x:%x Prefix len %d \n", |
| 318 | ip6rd->prefix.s6_addr32[0], ip6rd->prefix.s6_addr32[1], |
| 319 | ip6rd->prefix.s6_addr32[2], ip6rd->prefix.s6_addr32[3], |
| 320 | ip6rd->prefixlen); |
| 321 | |
| 322 | |
| 323 | nss_tun6rd_trace("Sending Tunnle 6rd Down command %x \n",g_tun6rd.if_num); |
| 324 | status = nss_tx_generic_if_buf(g_tun6rd.nss_ctx, |
| 325 | g_tun6rd.if_num, |
| 326 | (uint8_t *)&tun6rdparam, |
| 327 | sizeof(struct nss_tunnel_6rd_param)); |
| 328 | |
| 329 | if (status != NSS_TX_SUCCESS) { |
| 330 | nss_tun6rd_error("Tunnel down command error %d \n", status); |
| 331 | return; |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | * Un-Register 6rd tunnel with NSS |
| 336 | */ |
| 337 | nss_unregister_tun6rd_if(g_tun6rd.if_num); |
| 338 | g_tun6rd.nss_ctx = NULL; |
| 339 | g_tun6rd.device_up = 0; |
| 340 | return; |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | * nss_tun6rd_dev_event() |
| 345 | * Net device notifier for 6rd module |
| 346 | */ |
| 347 | static int nss_tun6rd_dev_event(struct notifier_block *nb, |
| 348 | unsigned long event, void *dev) |
| 349 | { |
| 350 | struct net_device *netdev = (struct net_device *)dev; |
| 351 | |
| 352 | nss_tun6rd_trace("%s\n",__FUNCTION__); |
| 353 | switch (event) { |
| 354 | case NETDEV_UP: |
| 355 | nss_tun6rd_trace(" NETDEV_UP :event %lu name %s \n", |
| 356 | event,netdev->name); |
| 357 | nss_tun6rd_dev_up(netdev); |
| 358 | break; |
| 359 | |
| 360 | case NETDEV_DOWN: |
| 361 | nss_tun6rd_trace(" NETDEV_DOWN :event %lu name %s \n", |
| 362 | event,netdev->name); |
| 363 | nss_tun6rd_dev_down(netdev); |
| 364 | break; |
| 365 | |
| 366 | default: |
| 367 | nss_tun6rd_trace("Unhandled notifier dev %s event %x \n", |
| 368 | netdev->name,(int)event); |
| 369 | break; |
| 370 | } |
| 371 | |
| 372 | return NOTIFY_DONE; |
| 373 | } |
| 374 | |
| 375 | /* |
| 376 | * nss_tun6rd_exception() |
| 377 | * Exception handler registered to NSS driver |
| 378 | */ |
| 379 | void nss_tun6rd_exception(void *ctx, void *buf) |
| 380 | { |
| 381 | struct net_device *dev = (struct net_device *)ctx; |
| 382 | struct sk_buff *skb = (struct sk_buff *)buf; |
| 383 | const struct iphdr *iph; |
| 384 | |
| 385 | skb->dev = dev; |
| 386 | nss_tun6rd_info("received - %d bytes name %s ver %x \n", |
| 387 | skb->len,dev->name,skb->data[0]); |
| 388 | |
| 389 | iph = (const struct iphdr *)skb->data; |
| 390 | |
| 391 | /* |
| 392 | * Packet after Decap/Encap Did not find the Rule. |
| 393 | */ |
| 394 | if (iph->version == 4) { |
| 395 | skb->protocol = htons(ETH_P_IP); |
| 396 | } else { |
| 397 | skb->protocol = htons(ETH_P_IPV6); |
| 398 | } |
| 399 | |
| 400 | skb_reset_network_header(skb); |
| 401 | skb->pkt_type = PACKET_HOST; |
| 402 | skb->skb_iif = dev->ifindex; |
| 403 | skb->ip_summed = CHECKSUM_NONE; |
| 404 | netif_receive_skb(skb); |
| 405 | } |
| 406 | |
| 407 | /* |
| 408 | * nss_tun6rd_init_module() |
| 409 | * Tunnel 6rd module init function |
| 410 | */ |
| 411 | int __init nss_tun6rd_init_module(void) |
| 412 | { |
| 413 | nss_tun6rd_info("module (platform - IPQ806x , Build - %s:%s) loaded\n", |
| 414 | __DATE__, __TIME__); |
| 415 | |
| 416 | register_netdevice_notifier(&nss_tun6rd_notifier); |
| 417 | nss_tun6rd_trace("Netdev Notifier registerd \n"); |
| 418 | |
| 419 | g_tun6rd.if_num = NSS_TUNRD_IF_NUMBER; |
| 420 | g_tun6rd.netdev = NULL; |
| 421 | g_tun6rd.device_up = 0; |
| 422 | g_tun6rd.nss_ctx = NULL; |
| 423 | |
| 424 | return 0; |
| 425 | } |
| 426 | |
| 427 | /* |
| 428 | * nss_tun6rd_exit_module() |
| 429 | * Tunnel 6rd module exit function |
| 430 | */ |
| 431 | void __exit nss_tun6rd_exit_module(void) |
| 432 | { |
| 433 | |
| 434 | unregister_netdevice_notifier(&nss_tun6rd_notifier); |
| 435 | nss_tun6rd_info("module unloaded\n"); |
| 436 | } |
| 437 | |
| 438 | module_init(nss_tun6rd_init_module); |
| 439 | module_exit(nss_tun6rd_exit_module); |
| 440 | |
| 441 | MODULE_LICENSE("Dual BSD/GPL"); |
| 442 | MODULE_DESCRIPTION("NSS tun6rd offload manager"); |