/* * Copyright (C) 2012-2016 Texas Instruments Incorporated * Authors: Sandeep Nair * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * * This program is distributed "as is" WITHOUT ANY WARRANTY of any * kind, whether express or implied; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "netcp.h" #define NET_SA_CHAN_PRFX "satx" #define PASAHO_SA_SHORT_INFO 1 /* Format the entire short info command */ #define PASAHO_SINFO_FORMAT_CMD(offset, len) \ (((offset) << 16) | (len) | (PASAHO_SA_SHORT_INFO << 29)) #define SA_RXHOOK_ORDER 30 #define ETH_TYPE_OFFSET 12 #define ETH_HDR_LEN 14 /* Version 0 of sa_rx_hook definitions (for NETCP 1.0 on K2HK) */ #define BITS(x) (BIT(x) - 1) #define BITMASK(n, s) (BITS(n) << (s)) #define SA_IS_IPSEC_ESP_MASK BIT(25) #define SA_IS_FLAG_FRAG_MASK BIT(3) #define SA_NUM_IP_HEADERS_SHIFT 8 #define SA_NUM_IP_HEADERS_MASK BITMASK(3, SA_NUM_IP_HEADERS_SHIFT) #define SA_NUM_VLAN_HEADERS_SHIFT 14 #define SA_NUM_VLAN_HEADERS_MASK BITMASK(2, SA_NUM_VLAN_HEADERS_SHIFT) #define SA_TXHOOK_ORDER 30 #define SA_IPSECMAN_MAJIC 0xdeadfeed /* Version 1 of sa_rx_hook definitions (for NSS 1.0 on K2L and K2E) */ #define SA_IS_IPSEC_ESP_MASK_VER1 BIT(8) #define SA_IS_FLAG_FRAG_MASK_VER1 BIT(19) #define SA_NUM_IP_HEADERS_SHIFT_VER1 0 #define SA_NUM_IP_HEADERS_MASK_VER1 BITMASK(3, SA_NUM_IP_HEADERS_SHIFT_VER1) #define SA_NUM_VLAN_HEADERS_SHIFT_VER1 6 #define SA_NUM_VLAN_HEADERS_MASK_VER1 \ BITMASK(2, SA_NUM_VLAN_HEADERS_SHIFT_VER1) #define FLOW_ID_SHIFT 16 #define SWINFO2_MASK 0xff000000 struct sa_device { struct device *dev; struct netcp_device *netcp_device; struct device_node *node; u32 tx_queue_id; u32 multi_if; }; struct sa_intf { struct net_device *net_device; const char *tx_chan_name; struct netcp_tx_pipe tx_pipe; u32 natt_port; u32 netcp_ver; /* 0: NETCP 1.0, 1: NSS 1.0 */ }; struct ipsecmgr_mod_sa_ctx_info { atomic_t refcnt; int len; int majic; u32 word0; u32 word1; u16 flow_id; }; static inline void calc_ipv6_esp_info(const struct sk_buff *skb, u16 *p_offset, u16 *p_len) { const struct ipv6hdr *ipv6h = ipv6_hdr(skb); /* type of the 1st exthdr */ WARN_ON(ipv6_hdr(skb)->nexthdr != NEXTHDR_ESP); *p_offset = (u16)(skb_network_header(skb) - skb->data) + skb_network_header_len(skb); /* payload length */ *p_len = ntohs(ipv6h->payload_len); } static int sa_tx_hook(int order, void *data, struct netcp_packet *p_info) { struct sa_intf *sa_intf = data; u16 offset, len, ihl; u32 *psdata, *swinfo; const struct iphdr *iph; struct ipsecmgr_mod_sa_ctx_info *ctx_info = (struct ipsecmgr_mod_sa_ctx_info *)p_info->skb->sp; if ((!ctx_info) || (ctx_info->majic != SA_IPSECMAN_MAJIC)) return 0; iph = ip_hdr(p_info->skb); if (iph->version == IPVERSION) { ihl = iph->ihl * 4; if (iph->protocol == IPPROTO_UDP) { /* UDP encapsulation for IPSec NAT-T */ offset = (ulong)(skb_network_header(p_info->skb) - p_info->skb->data) + ihl + sizeof(struct udphdr); len = ntohs(iph->tot_len) - ihl - sizeof(struct udphdr); } else if (iph->protocol == IPPROTO_ESP) { offset = (ulong)(skb_network_header(p_info->skb) - p_info->skb->data) + ihl; len = ntohs(iph->tot_len) - ihl; } else { return 0; } } else if (iph->version == 6) { if ((ipv6_hdr(p_info->skb)->nexthdr) == IPPROTO_ESP) /* need to calculate offset to where ESP header * starts and total length of the payload */ calc_ipv6_esp_info(p_info->skb, &offset, &len); else return 0; } else { return 0; } psdata = netcp_push_psdata(p_info, (2 * sizeof(u32))); if (!psdata) return -ENOMEM; psdata[0] = PASAHO_SINFO_FORMAT_CMD(offset, len); psdata[1] = 0; swinfo = &p_info->epib[1]; swinfo[0] = ctx_info->word0 | BIT(30); swinfo[1] = ctx_info->word1; swinfo[2] &= SWINFO2_MASK; swinfo[2] |= (ctx_info->flow_id) << FLOW_ID_SHIFT | p_info->tx_pipe->dma_queue_id; p_info->tx_pipe = &sa_intf->tx_pipe; ctx_info->len = 0; secpath_reset(p_info->skb); return 0; } static int sa_rx_hook(int order, void *data, struct netcp_packet *p_info) { struct sk_buff *skb = p_info->skb; struct sa_intf *sa = data; /* See if packet is a fragment, if it is not then return out. * This is word 3, bit 3 of metadata psdata */ if (!(p_info->psdata[3] & SA_IS_FLAG_FRAG_MASK)) return 0; /* Make sure there is just one IP header. If 2 ipheaders then * we passed IPSEC; so return out. */ if (((p_info->psdata[3] & SA_NUM_IP_HEADERS_MASK) >> SA_NUM_IP_HEADERS_SHIFT) != 1) return 0; /* Check to see if this is an ESP packet. * ESP packet indication is in word 3, bit 25 of psdata */ if (p_info->psdata[3] & SA_IS_IPSEC_ESP_MASK) { /* Packet is ESP, a fragment and does not have 2 * ipheaders, so set ignore_df flag */ skb->ignore_df = 1; return 0; } /* If we have a NAT-T port set, check for NAT-T packet */ if (sa->natt_port) { struct iphdr *ip_hdr; u16 e_type; int vlan; /* Count how many VLAN headers we have */ vlan = ((p_info->psdata[3] & SA_NUM_VLAN_HEADERS_MASK) >> SA_NUM_VLAN_HEADERS_SHIFT); /* Return out for IPv6, nothing to be done. * Ethertype follows the MAC and optional VLAN tags. */ e_type = *((u16 *)skb->data + ((ETH_TYPE_OFFSET / 2) + (vlan << 1))); if (ntohs(e_type) == ETH_P_IPV6) return 0; /* Skip over Eth header to IP header. * Eth header is 14 bytes + (4 bytes per VLAN header). */ ip_hdr = (struct iphdr *)(skb->data + (ETH_HDR_LEN + (vlan << 2))); /* Check if the protocol is UDP */ if (ip_hdr->protocol == IPPROTO_UDP) { /* Get the UDP header. * It follows the IP header + optional words; * calculate offset with IHL */ struct udphdr *udp_hdr; udp_hdr = (struct udphdr *) ((u_char *)ip_hdr + (ip_hdr->ihl << 2)); /* Check if the source or destination ports == * NAT-T port. If so, it is a NAT-T packet; * set ignore_df flag */ if (ntohs(udp_hdr->dest) == sa->natt_port || ntohs(udp_hdr->source) == sa->natt_port) skb->ignore_df = 1; } } return 0; } static int sa_rx_hook_ver1(int order, void *data, struct netcp_packet *p_info) { struct sk_buff *skb = p_info->skb; struct sa_intf *sa = data; /* See if packet is a fragment, if it is not then return out. * This is word 0, bit 19 of metadata psdata */ if (!(p_info->psdata[0] & SA_IS_FLAG_FRAG_MASK_VER1)) return 0; /* Make sure there is just one IP header. If 2 ipheaders then * we passed IPSEC; so return out. */ if (((p_info->psdata[4] & SA_NUM_IP_HEADERS_MASK_VER1) >> SA_NUM_IP_HEADERS_SHIFT_VER1) != 1) return 0; /* Check to see if this is an ESP packet. * ESP packet indication is in word 3, bit 8 of psdata */ if (p_info->psdata[3] & SA_IS_IPSEC_ESP_MASK_VER1) { /* Packet is ESP, a fragment and does not have 2 * ipheaders, so set ignore_df flag */ skb->ignore_df = 1; return 0; } /* If we have a NAT-T port set, check for NAT-T packet */ if (sa->natt_port) { struct iphdr *ip_hdr; u16 e_type; int vlan; /* Count how many VLAN headers we have */ vlan = ((p_info->psdata[4] & SA_NUM_VLAN_HEADERS_MASK_VER1) >> SA_NUM_VLAN_HEADERS_SHIFT_VER1); /* Return out for IPv6, nothing to be done. * Ethertype follows the MAC and optional VLAN tags. */ e_type = *((u16 *)skb->data + ((ETH_TYPE_OFFSET / 2) + (vlan << 1))); if (ntohs(e_type) == ETH_P_IPV6) return 0; /* Skip over Eth header to IP header. * Eth header is 14 bytes + (4 bytes per VLAN header). */ ip_hdr = (struct iphdr *)(skb->data + (ETH_HDR_LEN + (vlan << 2))); /* Check if the protocol is UDP */ if (ip_hdr->protocol == IPPROTO_UDP) { /* Get the UDP header. * It follows the IP header; calculate with IHL */ struct udphdr *udp_hdr; udp_hdr = (struct udphdr *) ((u_char *)ip_hdr + (ip_hdr->ihl << 2)); /* check if the source or destination ports == * NAT-T port. If so, it is a NAT-T packet; * set ignore_df flag */ if (ntohs(udp_hdr->dest) == sa->natt_port || ntohs(udp_hdr->source) == sa->natt_port) skb->ignore_df = 1; } } return 0; } static int sa_close(void *intf_priv, struct net_device *ndev) { struct sa_intf *sa_intf = (struct sa_intf *)intf_priv; struct netcp_intf *netcp_intf = netdev_priv(ndev); netcp_unregister_txhook(netcp_intf, SA_TXHOOK_ORDER, sa_tx_hook, sa_intf); if (sa_intf->netcp_ver == 1) netcp_unregister_rxhook(netcp_intf, SA_RXHOOK_ORDER, sa_rx_hook_ver1, sa_intf); else netcp_unregister_rxhook(netcp_intf, SA_RXHOOK_ORDER, sa_rx_hook, sa_intf); netcp_txpipe_close(&sa_intf->tx_pipe); return 0; } static int sa_open(void *intf_priv, struct net_device *ndev) { struct sa_intf *sa_intf = (struct sa_intf *)intf_priv; struct netcp_intf *netcp_intf = netdev_priv(ndev); int ret; /* Open the SA IPSec data transmit channel */ ret = netcp_txpipe_open(&sa_intf->tx_pipe); if (ret) return ret; netcp_register_txhook(netcp_intf, SA_TXHOOK_ORDER, sa_tx_hook, sa_intf); if (sa_intf->netcp_ver == 1) netcp_register_rxhook(netcp_intf, SA_RXHOOK_ORDER, sa_rx_hook_ver1, sa_intf); else netcp_register_rxhook(netcp_intf, SA_RXHOOK_ORDER, sa_rx_hook, sa_intf); return 0; } static int sa_attach(void *inst_priv, struct net_device *ndev, struct device_node *node, void **intf_priv) { struct sa_device *sa_dev = inst_priv; struct sa_intf *sa_intf; int ret = 0; sa_intf = devm_kzalloc(sa_dev->dev, sizeof(struct sa_intf), GFP_KERNEL); if (!sa_intf) return -ENOMEM; ret = of_property_read_string(node, "ti,tx-channel", &sa_intf->tx_chan_name); if (ret < 0) { dev_err(sa_dev->dev, "missing \"ti,tx-channel\" parameter\n"); return -EINVAL; } sa_intf->net_device = ndev; *intf_priv = sa_intf; netcp_txpipe_init(&sa_intf->tx_pipe, sa_dev->netcp_device, sa_intf->tx_chan_name, sa_dev->tx_queue_id); dev_dbg(sa_dev->dev, "keystone-sa attached for %s\n", node->name); ret = of_property_read_u32(sa_dev->node, "natt-port", &sa_intf->natt_port); if (ret < 0) sa_intf->natt_port = 0; ret = of_property_read_u32(sa_dev->node, "netcp_ver", &sa_intf->netcp_ver); if (ret < 0) sa_intf->netcp_ver = 0; return 0; } static int sa_release(void *intf_priv) { struct sa_intf *sa_intf = intf_priv; kfree(sa_intf); return 0; } static int sa_remove(struct netcp_device *netcp_device, void *inst_priv) { struct sa_device *sa_dev = inst_priv; kfree(sa_dev); return 0; } static int sa_probe(struct netcp_device *netcp_device, struct device *dev, struct device_node *node, void **inst_priv) { struct sa_device *sa_dev; int ret = 0; if (!node) { dev_err(dev, "device tree info unavailable\n"); return -ENODEV; } sa_dev = devm_kzalloc(dev, sizeof(struct sa_device), GFP_KERNEL); if (!sa_dev) return -ENOMEM; *inst_priv = sa_dev; sa_dev->dev = dev; sa_dev->netcp_device = netcp_device; sa_dev->node = node; if (of_find_property(node, "multi-interface", NULL)) sa_dev->multi_if = 1; ret = of_property_read_u32(node, "tx-submit-queue", &sa_dev->tx_queue_id); if (ret < 0) { dev_err(dev, "missing tx-submit-queue parameter, err %d\n", ret); return -EINVAL; } dev_dbg(dev, "tx-submit-queue %u\n", sa_dev->tx_queue_id); return 0; } static struct netcp_module sa_module = { .name = "netcp-sa", .owner = THIS_MODULE, .probe = sa_probe, .open = sa_open, .close = sa_close, .remove = sa_remove, .attach = sa_attach, .release = sa_release, }; static int __init keystone_sa_init(void) { return netcp_register_module(&sa_module); } module_init(keystone_sa_init); static void __exit keystone_sa_exit(void) { netcp_unregister_module(&sa_module); } module_exit(keystone_sa_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("IPSec driver for Keystone devices");