diff --git a/net/key/af_key.c b/net/key/af_key.c index c56bb4f451e6..7225be6880e1 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -26,8 +26,184 @@ #include #include #include +#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) +#include +#endif #include +#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) +#include +#define NLKEY_SUPPORT 1 +#else +#undef NLKEY_SUPPORT +#endif + +#ifdef NLKEY_SUPPORT +#include +#include +#include +#include + + +extern int xfrm_get_tos(struct flowi *fl, int family); + + +#define NLKEY_SA_CREATE 0x0A01 +#define NLKEY_SA_DELETE 0x0A02 +#define NLKEY_SA_FLUSH 0x0A03 +#define NLKEY_SA_SET_KEYS 0x0A04 +#define NLKEY_SA_SET_TUNNEL 0x0A05 +#define NLKEY_SA_SET_NATT 0x0A06 +#define NLKEY_SA_SET_STATE 0x0A07 +#define NLKEY_SA_SET_LIFETIME 0x0A08 +#define NLKEY_SA_NOTIFY 0x0A09 +#define NLKEY_SA_INFO_UPDATE 0x0A0C +#define NLKEY_SA_SET_OFFLOAD 0x0A0D +#define NLKEY_FLOW_ADD 0x0A11 +#define NLKEY_FLOW_REMOVE 0x0A12 +#define NLKEY_FLOW_NOTIFY 0x0A13 +#define NLKEY_NULL_MSG 0x0000 + +#define NLKEY_HDR_LEN 4 +#define NLKEY_MSG_LEN 256 + +#define NLKEY_MAX_NUM_KEYS 2 +#define NLKEY_MAX_KEY_LEN (512 / 8) + +struct nlkey_msg { + /* message data */ + unsigned short fcode; + unsigned short length; + unsigned short payload[(NLKEY_MSG_LEN /sizeof(unsigned short))]; +}; +/* sizeof(nlkey_msg) = 4 + 256 */ + +struct nlkey_sa_id { + unsigned int spi; + unsigned char sa_type; + unsigned char proto_family; + unsigned char replay_window; +#define NLKEY_SAFLAGS_ESN 0x1 +#define NLKEY_SAFLAGS_INBOUND 0x2 + unsigned char flags; + unsigned int dst_ip[4]; + unsigned int src_ip[4]; + unsigned short mtu; + unsigned short dev_mtu; + +}; +/* sizeof(nlkey_sa_id) = 24 */ + +struct nlkey_sa_create { + unsigned short sagd; + unsigned short parent_sa_sagd; /*sagd value of old SA from which this SA is rekeyed.*/ + struct nlkey_sa_id said; +}; +/* sizeof(nlkey_sa_delete) = 28 */ + +struct nlkey_sa_delete { + unsigned short sagd; + unsigned short rsvd; +}; +/* sizeof(nlkey_sa_delete) = 4 */ + +struct nlkey_sa_set_tunnel { + unsigned short sagd; + unsigned char rsvd; + unsigned char proto_family; + union { + struct iphdr ipv4h; + struct ipv6hdr ipv6h; + } h; +}; +/* sizeof(nlkey_sa_set_tunnel) = 36 */ + +struct nlkey_sa_set_natt { + unsigned short sagd; + unsigned short sport; + unsigned short dport; + unsigned short rsvd; +}; +/* sizeof(nlkey_sa_set_natt) = 4 */ + +struct nlkey_sa_set_state { + unsigned short sagd; + unsigned short parent_sa_sagd; + unsigned short state; + unsigned short rsvd2; +}; +/* sizeof(nlkey_sa_set_natt) = 8 */ + +struct nlkey_key_desc { + unsigned short key_bits; + unsigned char key_alg; + unsigned char key_type; + unsigned char key[NLKEY_MAX_KEY_LEN]; +}; +/* sizeof(nlkey_key_desc) = 36 */ + +struct nlkey_sa_set_keys { + unsigned short sagd; + unsigned short rsvd; + unsigned short num_keys; + unsigned short rsvd2; + struct nlkey_key_desc keys[NLKEY_MAX_NUM_KEYS]; +}; +/* sizeof(nlkey_sa_set_keys) = 80 */ + +struct nlkey_lifetime_desc { + unsigned int allocations; + unsigned int bytes[2]; +}; +/* sizeof(nlkey_sa_set_lifetime) = 12 */ + +struct nlkey_sa_set_lifetime { + unsigned short sagd; + unsigned short rsvd; + struct nlkey_lifetime_desc hard_time; + struct nlkey_lifetime_desc soft_time; + struct nlkey_lifetime_desc current_time; +}; +/* sizeof(nlkey_sa_set_lifetime) = 40 */ + +/* SA notifications */ +#define IPSEC_SOFT_EXPIRE 0 +#define IPSEC_HARD_EXPIRE 1 + +struct nlkey_sa_notify { + unsigned short sagd; + unsigned short rsvd; + unsigned int action; +}; +/* sizeof(nlkey_sa_notify) = 8 */ + +/* SA Info update */ + +struct nlkey_sa_info { + unsigned short sagd; + unsigned short rsvd; + unsigned long long bytes; + unsigned long long packets; +}; +/* sizeof(nlkey_sa_info) = */ + + +static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c); +static void ipsec_nlkey_rcv(struct sk_buff *skb); +static void ipsec_nlkey_init(void); +static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code); +static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, + const struct km_event *c, unsigned short *msg_id); +static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, const struct km_event *c, struct nlkey_sa_id *said); + +void flow_cache_remove(const struct flowi *fl, unsigned short family, + unsigned short dir); +/* netlink NETLINK_KEY socket */ +struct sock *nlkey_socket = NULL; + +#endif +/************************************************************************************/ + #define _X2KEY(x) ((x) == XFRM_INF ? 0 : (x)) #define _KEY2X(x) ((x) == 0 ? XFRM_INF : (x)) @@ -876,6 +1051,10 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, sa->sadb_sa_flags |= SADB_SAFLAGS_DECAP_DSCP; if (x->props.flags & XFRM_STATE_NOPMTUDISC) sa->sadb_sa_flags |= SADB_SAFLAGS_NOPMTUDISC; +#ifdef NLKEY_SUPPORT + if (x->props.flags & XFRM_STATE_ESN) + sa->sadb_sa_flags |= SADB_SAFLAGS_ESN; +#endif /* hard time */ if (hsc & 2) { @@ -908,6 +1087,11 @@ static struct sk_buff *__pfkey_xfrm_state2msg(const struct xfrm_state *x, lifetime->sadb_lifetime_bytes = x->curlft.bytes; lifetime->sadb_lifetime_addtime = x->curlft.add_time; lifetime->sadb_lifetime_usetime = x->curlft.use_time; + +#if defined(CONFIG_INET_IPSEC_OFFLOAD)|| defined(CONFIG_INET6_IPSEC_OFFLOAD) + lifetime->sadb_lifetime_usetime = x->curr_time; +#endif + /* src address */ addr = skb_put(skb, sizeof(struct sadb_address) + sockaddr_size); addr->sadb_address_len = @@ -1133,6 +1317,10 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net, x->props.flags |= XFRM_STATE_DECAP_DSCP; if (sa->sadb_sa_flags & SADB_SAFLAGS_NOPMTUDISC) x->props.flags |= XFRM_STATE_NOPMTUDISC; +#ifdef NLKEY_SUPPORT + if (sa->sadb_sa_flags & SADB_SAFLAGS_ESN) + x->props.flags |= XFRM_STATE_ESN; +#endif lifetime = ext_hdrs[SADB_EXT_LIFETIME_HARD - 1]; if (lifetime != NULL) { @@ -3076,6 +3264,12 @@ static int pfkey_send_notify(struct xfrm_state *x, const struct km_event *c) struct net *net = x ? xs_net(x) : c->net; struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); + +#ifdef NLKEY_SUPPORT + /* send message to the user space through NETLINK_KEY socket*/ + ipsec_nlkey_send(net, x, c); +#endif + if (atomic_read(&net_pfkey->socks_nr) == 0) return 0; @@ -3863,6 +4057,687 @@ static struct xfrm_mgr pfkeyv2_mgr = .is_alive = pfkey_is_alive, }; + +#ifdef NLKEY_SUPPORT +extern struct xfrm_state *xfrm_state_lookup_byhandle(struct net *net, u16 handle); + +static unsigned short ipsec_sacode_to_nlkeycode(unsigned short sa_code) +{ + unsigned nlkey_code; + + switch (sa_code) + { + case XFRM_MSG_DELSA: + nlkey_code = NLKEY_SA_DELETE; + break; + case XFRM_MSG_NEWSA: + case XFRM_MSG_UPDSA: + nlkey_code = NLKEY_SA_CREATE; + break; + case XFRM_MSG_FLUSHSA: + nlkey_code = NLKEY_SA_FLUSH; + break; + case XFRM_MSG_EXPIRE: + nlkey_code = NLKEY_SA_SET_STATE; + break; + default: + nlkey_code = NLKEY_NULL_MSG; + break; + } + + return nlkey_code; +} + +static void ipsec_nlkey_rcv(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + struct nlkey_msg *msg = NULL; + struct flowi flow; + unsigned short *p; + unsigned short family, dir; + struct xfrm_state *x; + struct nlkey_sa_notify sa_notify_msg; + struct nlkey_sa_info sa_info_msg; + + /* extract message from skb */ + nlh = (struct nlmsghdr *)skb->data; + + msg = (struct nlkey_msg *)NLMSG_DATA(nlh); + + //printk(KERN_INFO "ipsec_nlkey_rcv fcode: 0x%x length: %d bytes\n",msg->fcode,msg->length); + + /* process command received from user space */ + switch(msg->fcode) + { + case NLKEY_FLOW_REMOVE: + //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_FLOW_REMOVE\n"); + p = msg->payload; + memcpy(&flow, p, sizeof(struct flowi)); p += sizeof(struct flowi)/2; + family = *p; p++; + dir = *p; p++; + flow_cache_remove(&flow, family, dir); + break; + + case NLKEY_SA_NOTIFY: + //printk(KERN_INFO "ipsec_nlkey_rcv NLKEY_SA_NOTIFY\n"); + memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify)); + x = xfrm_state_lookup_byhandle(&init_net, sa_notify_msg.sagd); + if (x) { + spin_lock(&x->lock); + + if (sa_notify_msg.action) { + // hard expired + x->km.state = XFRM_STATE_EXPIRED; + hrtimer_start(&x->mtimer, ktime_set(0,0), HRTIMER_MODE_REL_SOFT); + } + else if (!x->km.dying) { + x->km.dying = 1; + km_state_expired(x, 0, 0); + } + + spin_unlock(&x->lock); + xfrm_state_put(x); + } + break; + + case NLKEY_SA_INFO_UPDATE: + memcpy(&sa_info_msg, msg->payload, sizeof(struct nlkey_sa_info)); + + x = xfrm_state_lookup_byhandle(&init_net,sa_info_msg.sagd); + if (x) { + spin_lock(&x->lock); + + if (x->curlft.bytes != sa_info_msg.bytes) + x->curr_time = ktime_get_real_seconds(); + + x->curlft.bytes = sa_info_msg.bytes; + x->curlft.packets = sa_info_msg.packets; + + spin_unlock(&x->lock); + xfrm_state_put(x); + } + break; + + case NLKEY_SA_SET_OFFLOAD: + memcpy(&sa_notify_msg, msg->payload, sizeof(struct nlkey_sa_notify)); + x = xfrm_state_lookup_byhandle(&init_net,sa_notify_msg.sagd); + if (x) { + spin_lock(&x->lock); + if(sa_notify_msg.action) + x->offloaded = 1; + else + x->offloaded = 0; + spin_unlock(&x->lock); + xfrm_state_put(x); + } + break; + default: + //printk(KERN_INFO "ipsec_nlkey_rcv fcode 0x%x not supported\n", msg->fcode); + break; + } + +} +static int ipsec_nlkey_set_said(struct net *net, struct xfrm_state *x, + const struct km_event *c, struct nlkey_sa_id *said) +{ + + struct flowi fl; + int tos; + xfrm_address_t saddr, daddr; + struct dst_entry *dst; + struct rt6_info *rt; + int rc = 0; + int oif = 0; + + memset(&fl, 0, sizeof(struct flowi)); + + /* SPI */ + said->spi = x->id.spi; + /* SA Type (AH or ESP) */ + said->sa_type = x->id.proto; + /* Protocol Family (IPv4 or IPv6) */ + said->proto_family = x->props.family; + /* Replay window */ + said->replay_window = x->props.replay_window; + /* Destination IP Address */ + if(x->props.family == AF_INET6) { + memcpy(&said->dst_ip, x->id.daddr.a6, sizeof(struct in6_addr)); + fl.u.ip6.daddr = *(struct in6_addr *)x->id.daddr.a6; + memcpy(&said->src_ip, x->props.saddr.a6, sizeof(struct in6_addr)); + } + else { + said->dst_ip[0] = x->id.daddr.a4; + fl.u.ip4.daddr = x->id.daddr.a4; + said->src_ip[0] = x->props.saddr.a4; + } + said->mtu = 0; + + if(x->props.flags & XFRM_STATE_ESN) + said->flags = NLKEY_SAFLAGS_ESN; + xfrm_flowi_addr_get(&fl, &saddr, &daddr, x->props.family); + + tos = xfrm_get_tos(&fl, x->props.family); + if (tos < 0) { + printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); + rc = -1; + goto error; + } + + switch (x->props.family) + { + case AF_INET: + if (!__ip_route_output_key(net, &(fl.u.ip4))) + { + printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); + rc = -1; + goto error; + } + oif = fl.u.ip4.flowi4_oif; + break; + + case AF_INET6: + rt = rt6_lookup(net, &fl.u.ip6.daddr, NULL, 0, NULL, 0); + if ((!rt) || (!rt->dst.dev)) + { + printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); + rc = -1; + goto error; + } + oif = rt->dst.dev->ifindex; + break; + } + + { + struct xfrm_dst_lookup_params params = { + .net = net, + .dscp = inet_dsfield_to_dscp(tos), + .oif = oif, + .saddr = NULL, + .daddr = &daddr, + .mark = xfrm_smark_get(0, x), + }; + dst = __xfrm_dst_lookup(x->props.family, ¶ms); + } + if (IS_ERR(dst)) { + printk(KERN_ERR "%s:%d: FIXME\n",__func__,__LINE__); + rc = -1; + goto error; + } + + if (strcmp(dst->dev->name, "lo") == 0) + said->flags |= NLKEY_SAFLAGS_INBOUND; + + said->dev_mtu = dst_mtu(dst); + said->mtu = xfrm_state_mtu(x,dst_mtu(dst)); + + dst_release(dst); +error: + return rc; +} + +static struct sk_buff * ipsec_xfrm2nlkey (struct net *net, struct xfrm_state *x, + const struct km_event *c, unsigned short *msg_id) +{ + struct nlkey_sa_id sa_id_msg; + struct nlkey_sa_create sa_create_msg; + struct nlkey_sa_delete sa_delete_msg; + struct nlkey_sa_set_keys sa_set_keys_msg; + struct nlkey_sa_set_tunnel sa_set_tunnel_msg; + struct nlkey_sa_set_natt sa_set_natt_msg; + struct nlkey_sa_set_state sa_set_state_msg; + struct nlkey_sa_set_lifetime sa_set_lifetime_msg; + struct nlkey_msg msg; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + gfp_t allocation = GFP_ATOMIC; //This may called from atomic context + unsigned char tunnel, keys, natt, state, lifetime; + + /* supported SA informations */ + keys = 1; state = 1; tunnel = 1; lifetime = 1; natt = 1; + + /* next message to build */ + memset(&msg, 0, sizeof(struct nlkey_msg)); + msg.fcode = *msg_id; + + //printk(KERN_INFO "\n\nipsec_xfrm2nlkey: processing event 0x%x\n", msg.fcode); + + switch (msg.fcode) + { + case NLKEY_SA_CREATE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_CREATE\n"); + if(x) { + /* some check before builing message */ + if(x->id.proto != IPPROTO_ESP) { + printk(KERN_ERR "protocol %d not supported in fast path.\n", x->id.proto); + *msg_id = NLKEY_NULL_MSG; + goto exit; + } + + memset(&sa_create_msg, 0, sizeof(struct nlkey_sa_create)); + + /* SA global handler */ + sa_create_msg.sagd = x->handle; + + sa_create_msg.parent_sa_sagd = x->parent_sa_handle; + + /* SA identifier */ + if(ipsec_nlkey_set_said(net, x, c, &sa_create_msg.said) < 0) + { + printk(KERN_ERR "%s: set sa ID failed\n", __func__); + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + memcpy(msg.payload, &sa_create_msg, sizeof(struct nlkey_sa_create)); + msg.length = sizeof(struct nlkey_sa_create); + *msg_id = NLKEY_SA_SET_KEYS; /* next message */ + } else { + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + + break; + + case NLKEY_SA_SET_KEYS: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_KEYS\n"); + if(keys) { + memset(&sa_set_keys_msg, 0, sizeof(struct nlkey_sa_set_keys)); + + /* SA global handler */ + sa_set_keys_msg.sagd = x->handle; + + /* auth key */ + if(x->aalg) { + if (x->aalg->alg_key_len) { + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aalg->alg_key_len; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.aalgo; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 0; + memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aalg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: AUTH - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); + sa_set_keys_msg.num_keys++; + } + } + /* encrypt key */ + if(x->ealg) { + if (x->ealg->alg_key_len) { + + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->ealg->alg_key_len; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = x->props.ealgo; + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1; + memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->ealg->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits / 8)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT - algo %d key %d bits\n", sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); + sa_set_keys_msg.num_keys++; + } + } + /* combined key */ + if (x->aead) { + if (x->aead->alg_key_len) { + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits = x->aead->alg_key_len; + if (strstr(x->aead->alg_name, "rfc4106(gcm")) /* AES GCM support */ + { + if (x->aead->alg_icv_len == 64) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV8; + else if (x->aead->alg_icv_len == 96) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV12; + else if (x->aead->alg_icv_len == 128) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_GCM_ICV16; + } + else if (strstr(x->aead->alg_name, "ccm")) /* AES CCM */ + { + if (x->aead->alg_icv_len == 64) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV8; + else if (x->aead->alg_icv_len == 96) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV12; + else if (x->aead->alg_icv_len == 128) + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_AES_CCM_ICV16; + } + else if (strstr(x->aead->alg_name, "rfc4543(gcm")) /* AES GMAC defined in RFC 4543 derived from AES GCM */ + { + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg = SADB_X_EALG_NULL_AES_GMAC; + } + + sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_type = 1; + memcpy(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key, x->aead->alg_key,(sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits/ 8)); + /* + printk(KERN_INFO "ipsec_xfrm2nlkey: ENCRYPT -alg name %s algo %d key %d bits\n", + x->aead->alg_name, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_alg, sa_set_keys_msg.keys[sa_set_keys_msg.num_keys].key_bits); + */ + sa_set_keys_msg.num_keys++; + } + } + + memcpy(msg.payload, &sa_set_keys_msg, sizeof(struct nlkey_sa_set_keys)); + msg.length = sizeof(struct nlkey_sa_set_keys); + *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_TUNNEL; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_TUNNEL: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_TUNNEL\n"); + if(tunnel && (x->props.mode == XFRM_MODE_TUNNEL)) { + memset(&sa_set_tunnel_msg, 0, sizeof(struct nlkey_sa_set_tunnel)); + + /* SA global handler */ + sa_set_tunnel_msg.sagd = x->handle; + + /* Tunnel */ + sa_set_tunnel_msg.proto_family = x->props.family; + if(x->props.family == AF_INET6) { + struct ipv6hdr *top_iph = &sa_set_tunnel_msg.h.ipv6h; + int dsfield; + top_iph->version = 6; + top_iph->priority = 0; + top_iph->flow_lbl[0] = 0; + top_iph->flow_lbl[1] = 0; + top_iph->flow_lbl[2] = 0; + top_iph->nexthdr = IPPROTO_IPIP; + dsfield = ipv6_get_dsfield(top_iph); + dsfield = INET_ECN_encapsulate(dsfield, dsfield); + if (x->props.flags & XFRM_STATE_NOECN) + dsfield &= ~INET_ECN_MASK; + ipv6_change_dsfield(top_iph, 0, dsfield); + top_iph->hop_limit = 64; + memcpy(&top_iph->daddr, x->id.daddr.a6, sizeof(struct in6_addr)); + memcpy(&top_iph->saddr, x->props.saddr.a6, sizeof(struct in6_addr)); + //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv6 tunnel\n"); + //printk(KERN_INFO "dst: %x %x %x %x\n", x->id.daddr.a6[0], x->id.daddr.a6[1], x->id.daddr.a6[2], x->id.daddr.a6[3]); + //(KERN_INFO "src: %x %x %x %x\n", x->props.saddr.a6[0], x->props.saddr.a6[1], x->props.saddr.a6[2], x->props.saddr.a6[3]); + } + else { + struct iphdr *top_iph = &sa_set_tunnel_msg.h.ipv4h; + top_iph->ihl = 5; + top_iph->version = 4; + top_iph->tos = 0; + top_iph->frag_off = 0; + top_iph->ttl = 64; + top_iph->saddr = x->props.saddr.a4; + top_iph->daddr = x->id.daddr.a4; + //printk(KERN_INFO "ipsec_xfrm2nlkey: IPv4 tunnel dst:%x - src:%x \n", x->id.daddr.a4, x->props.saddr.a4); + } + memcpy(msg.payload, &sa_set_tunnel_msg, sizeof(struct nlkey_sa_set_tunnel)); + msg.length = sizeof(struct nlkey_sa_set_tunnel); + *msg_id = NLKEY_SA_SET_NATT; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_NATT; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_NATT: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_NATT\n"); + if((natt) && (x->encap)){ + memset(&sa_set_natt_msg, 0, sizeof(struct nlkey_sa_set_natt)); + + /* SA global handler */ + sa_set_natt_msg.sagd = x->handle; + sa_set_natt_msg.sport = x->encap->encap_sport; + sa_set_natt_msg.dport = x->encap->encap_dport; + //printk(KERN_INFO "src port: %d dst port: %d \n", ntohs(sa_set_natt_msg.sport), ntohs( sa_set_natt_msg.dport)); + memcpy(msg.payload, &sa_set_natt_msg, sizeof(struct nlkey_sa_set_natt)); + msg.length = sizeof(struct nlkey_sa_set_natt); + *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_LIFETIME; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_LIFETIME: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_SET_LIFETIME\n"); + if(lifetime) { + memset(&sa_set_lifetime_msg, 0, sizeof(struct nlkey_sa_set_lifetime)); + + /* SA global handler */ + sa_set_lifetime_msg.sagd = x->handle; + + /* hard time */ + sa_set_lifetime_msg.hard_time.allocations = _X2KEY(x->lft.hard_packet_limit); + if(_X2KEY(x->lft.hard_byte_limit)) + memcpy(sa_set_lifetime_msg.hard_time.bytes, &x->lft.hard_byte_limit, sizeof(uint64_t)); + + /* soft time */ + sa_set_lifetime_msg.soft_time.allocations = _X2KEY(x->lft.soft_packet_limit); + if(_X2KEY(x->lft.soft_byte_limit)) + memcpy(sa_set_lifetime_msg.soft_time.bytes, &x->lft.soft_byte_limit, sizeof(uint64_t)); + + /* current time */ + sa_set_lifetime_msg.current_time.allocations = x->curlft.packets; + memcpy(sa_set_lifetime_msg.current_time.bytes, &x->curlft.bytes, sizeof(uint64_t)); + + memcpy(msg.payload, &sa_set_lifetime_msg, sizeof(struct nlkey_sa_set_lifetime)); + msg.length = sizeof(struct nlkey_sa_set_lifetime); + *msg_id = NLKEY_SA_SET_STATE; /* next message */ + } else { + *msg_id = NLKEY_SA_SET_STATE; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_SET_STATE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SET_STATE\n"); + if(state) { + memset(&sa_set_state_msg, 0, sizeof(struct nlkey_sa_set_state)); + memset(&sa_id_msg, 0, sizeof(struct nlkey_sa_id)); + + /* SA global handler */ + sa_set_state_msg.sagd = x->handle; + sa_set_state_msg.parent_sa_sagd = x->parent_sa_handle; + /* State */ + sa_set_state_msg.state = x->km.state; + // TODO: set the offloaded state once ack received ! + + memcpy(msg.payload, &sa_set_state_msg, sizeof(struct nlkey_sa_set_state)); + msg.length = sizeof(struct nlkey_sa_set_state); + *msg_id = NLKEY_NULL_MSG; /* next message */ + } else { + *msg_id = NLKEY_NULL_MSG; /* next message */ + goto exit; + } + break; + + case NLKEY_SA_DELETE: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_DELETE\n"); + memset(&sa_delete_msg, 0, sizeof(struct nlkey_sa_delete)); + + /* SA global handler */ + sa_delete_msg.sagd = x->handle; + memcpy(msg.payload, &sa_delete_msg, sizeof(struct nlkey_sa_delete)); + msg.length = sizeof(struct nlkey_sa_delete); + + + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + + case NLKEY_SA_FLUSH: + //printk(KERN_INFO "ipsec_xfrm2nlkey: NLKEY_SA_FLUSH\n"); + /* No data required for flush SA command */ + + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + + default: + printk(KERN_ERR "ipsec_xfrm2nlkey: event 0x%x not supported\n", c->event); + *msg_id = NLKEY_NULL_MSG; /* next message */ + break; + } + + /* prepare netlink message for kernel to user space direction */ + if(msg.length > NLKEY_MSG_LEN) + { + printk(KERN_ERR "ipsec_xfrm2nlkey: maximum message size reached (%d bytes)\n", msg.length); + goto exit; + } + + skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); + if (skb == NULL) + goto exit; + + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); + memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); + + /* whole length of the message i.e. header + payload */ + nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); + + /* from kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 1; +exit: + return skb; +} + +static int ipsec_nlkey_send(struct net *net, struct xfrm_state *x, const struct km_event *c) +{ + struct sk_buff *skb; + unsigned short msg_type; + int rc = 0; + + /* We may generate more than one message when adding new SA (sa_create + sa_set_state + sa_set_tunnel...) */ + msg_type = ipsec_sacode_to_nlkeycode((unsigned short)c->event); + + while(msg_type != NLKEY_NULL_MSG) + { + /* build nlkey message */ + skb = ipsec_xfrm2nlkey(net, x, c, &msg_type); + + if(skb != NULL) + if((rc = netlink_broadcast(nlkey_socket, skb, 0, 1, GFP_ATOMIC)) < 0) + return rc; + } + + return rc; +} + + +int ipsec_nlkey_flow(u16 xfrm_nr, u16 *xfrm_handle, const struct flowi *fl, u16 family, u16 dir, u16 ignore_neigh) +{ + struct sk_buff *skb; + struct nlkey_msg msg; + struct nlmsghdr *nlh = NULL; + unsigned short *p; + gfp_t allocation = GFP_ATOMIC; //This may called from atomic context + + //printk(KERN_INFO "ipsec_nlkey_flow \n"); + + /* next message to build */ + memset(&msg, 0, sizeof(struct nlkey_msg)); + msg.fcode = NLKEY_FLOW_ADD; + + // Number of SA for this flow + p = msg.payload; + *p++ = xfrm_nr; + msg.length += sizeof(unsigned short); + // SA handles list + memcpy(p, xfrm_handle, xfrm_nr*sizeof(unsigned short)); + msg.length += xfrm_nr*sizeof(unsigned short); + p+=xfrm_nr; + // flow family + *p++ = family; + msg.length += sizeof(unsigned short); + // flow family + *p++ = dir; + msg.length += sizeof(unsigned short); + // flow mode + *p++ = ignore_neigh; + msg.length += sizeof(unsigned short); + // flow descriptor + memcpy(p, fl, sizeof(struct flowi)); + msg.length +=sizeof(struct flowi); + p+=sizeof(struct flowi) / sizeof(u16); + + skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); + if (skb == NULL) + return -ENOMEM; + + /* prepare netlink message for kernel to user space direction */ + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); + memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); + + /* whole length of the message i.e. header + payload */ + nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); + + /* from kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 1; + + return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation)); +} +EXPORT_SYMBOL(ipsec_nlkey_flow); + + +int ipsec_nlkey_flow_remove(struct flowi *fl, u16 family, u16 dir) +{ + struct sk_buff *skb; + struct nlkey_msg msg; + struct nlmsghdr *nlh = NULL; + unsigned short *p; + gfp_t allocation = GFP_ATOMIC; //This may called from atomic context + + + //printk(KERN_INFO "ipsec_nlkey_flow_remove\n"); + + /* next message to build */ + memset(&msg, 0, sizeof(struct nlkey_msg)); + msg.fcode = NLKEY_FLOW_REMOVE; + + p = msg.payload; + // flow family + *p++ = family; + msg.length += sizeof(unsigned short); + // flow family + *p++ = dir; + msg.length += sizeof(unsigned short); + // flow descriptor + memcpy(p, fl, sizeof(struct flowi)); + msg.length +=sizeof(struct flowi); + p+=sizeof(struct flowi) / sizeof(u16); + + skb = alloc_skb(NLMSG_SPACE(NLKEY_MSG_LEN + NLKEY_HDR_LEN), allocation); + if (skb == NULL) + return -ENOMEM; + + /* prepare netlink message for kernel to user space direction */ + nlh = (struct nlmsghdr *)skb_put(skb, NLMSG_SPACE(NLKEY_HDR_LEN + msg.length)); + memcpy(NLMSG_DATA(nlh), (unsigned char *)&msg, (NLKEY_HDR_LEN + msg.length)); + + /* whole length of the message i.e. header + payload */ + nlh->nlmsg_len = NLMSG_SPACE(NLKEY_HDR_LEN + msg.length); + + /* from kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 1; + + + return(netlink_broadcast(nlkey_socket, skb, 0, 1, allocation)); + + +} +EXPORT_SYMBOL(ipsec_nlkey_flow_remove); + + + +static void ipsec_nlkey_init(void) +{ + struct netlink_kernel_cfg cfg = { + .groups = 1, + .input = ipsec_nlkey_rcv, + }; + printk(KERN_INFO "Initializing NETLINK_KEY socket\n"); + nlkey_socket = netlink_kernel_create(&init_net, NETLINK_KEY, &cfg); +} +#endif + + static int __net_init pfkey_net_init(struct net *net) { struct netns_pfkey *net_pfkey = net_generic(net, pfkey_net_id); @@ -3897,6 +4772,11 @@ static void __exit ipsec_pfkey_exit(void) sock_unregister(PF_KEY); unregister_pernet_subsys(&pfkey_net_ops); proto_unregister(&key_proto); + +#ifdef NLKEY_SUPPORT + /* release NETLINK_KEY socket */ + sock_release(nlkey_socket->sk_socket); +#endif } static int __init ipsec_pfkey_init(void) @@ -3913,6 +4793,12 @@ static int __init ipsec_pfkey_init(void) if (err != 0) goto out_unregister_pernet; xfrm_register_km(&pfkeyv2_mgr); + +#ifdef NLKEY_SUPPORT + /* create NETLINK_KEY socket for IPSec offload on Comcerto */ + ipsec_nlkey_init(); +#endif + out: return err;