/* * IPSEC Transmit code. * Copyright (C) 1996, 1997 John Ioannidis. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Richard Guy Briggs. * * 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; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ char ipsec_xmit_c_version[] = "RCSID $Id: ipsec_xmit.c,v 1.3 2004/06/13 19:37:23 as Exp $"; #define __NO_VERSION__ #include #include /* for CONFIG_IP_FORWARD */ #include #include /* printk() */ #include "freeswan/ipsec_param.h" #ifdef MALLOC_SLAB # include /* kmalloc() */ #else /* MALLOC_SLAB */ # include /* kmalloc() */ #endif /* MALLOC_SLAB */ #include /* error codes */ #include /* size_t */ #include /* mark_bh */ #include /* struct device, struct net_device_stats, dev_queue_xmit() and other headers */ #include /* eth_type_trans */ #include /* struct iphdr */ #include /* struct tcphdr */ #include /* struct udphdr */ #include #include #ifdef NET_21 # define MSS_HACK_ /* experimental */ # include # include # include # define proto_priv cb #endif /* NET_21 */ #include #include /* icmp_send() */ #include #ifdef NETDEV_23 # include #endif /* NETDEV_23 */ #include #ifdef MSS_HACK # include /* TCP options */ #endif /* MSS_HACK */ #include "freeswan/radij.h" #include "freeswan/ipsec_life.h" #include "freeswan/ipsec_xform.h" #include "freeswan/ipsec_eroute.h" #include "freeswan/ipsec_encap.h" #include "freeswan/ipsec_radij.h" #include "freeswan/ipsec_xmit.h" #include "freeswan/ipsec_sa.h" #include "freeswan/ipsec_tunnel.h" #include "freeswan/ipsec_ipe4.h" #include "freeswan/ipsec_ah.h" #include "freeswan/ipsec_esp.h" #ifdef CONFIG_IPSEC_IPCOMP #include "freeswan/ipcomp.h" #endif /* CONFIG_IPSEC_IPCOMP */ #include #include #include "freeswan/ipsec_proto.h" #include "freeswan/ipsec_alg.h" /* * Stupid kernel API differences in APIs. Not only do some * kernels not have ip_select_ident, but some have differing APIs, * and SuSE has one with one parameter, but no way of checking to * see what is really what. */ #ifdef SUSE_LINUX_2_4_19_IS_STUPID #define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph) #else /* simplest case, nothing */ #if !defined(IP_SELECT_IDENT) #define KLIPS_IP_SELECT_IDENT(iph, skb) do { iph->id = htons(ip_id_count++); } while(0) #endif /* kernels > 2.3.37-ish */ #if defined(IP_SELECT_IDENT) && !defined(IP_SELECT_IDENT_NEW) #define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst) #endif /* kernels > 2.4.2 */ #if defined(IP_SELECT_IDENT) && defined(IP_SELECT_IDENT_NEW) #define KLIPS_IP_SELECT_IDENT(iph, skb) ip_select_ident(iph, skb->dst, NULL) #endif #endif /* SUSE_LINUX_2_4_19_IS_STUPID */ static __u32 zeroes[64]; #ifdef CONFIG_IPSEC_DEBUG int sysctl_ipsec_debug_verbose = 0; #endif /* CONFIG_IPSEC_DEBUG */ int ipsec_xmit_trap_count = 0; int ipsec_xmit_trap_sendcount = 0; int sysctl_ipsec_icmp = 0; int sysctl_ipsec_tos = 0; /* * If the IP packet (iph) is a carrying TCP/UDP, then set the encaps * source and destination ports to those from the TCP/UDP header. */ void ipsec_extract_ports(struct iphdr * iph, struct sockaddr_encap * er) { struct udphdr *udp; switch (iph->protocol) { case IPPROTO_UDP: case IPPROTO_TCP: /* * The ports are at the same offsets in a TCP and UDP * header so hack it ... */ udp = (struct udphdr*)(((char*)iph)+(iph->ihl<<2)); er->sen_sport = udp->source; er->sen_dport = udp->dest; break; default: er->sen_sport = 0; er->sen_dport = 0; break; } } /* * A TRAP eroute is installed and we want to replace it with a HOLD * eroute. */ static int create_hold_eroute(struct sk_buff * skb, struct iphdr * iph, uint32_t eroute_pid) { struct eroute hold_eroute; struct sa_id hold_said; struct sk_buff *first, *last; int error; first = last = NULL; memset((caddr_t)&hold_eroute, 0, sizeof(hold_eroute)); memset((caddr_t)&hold_said, 0, sizeof(hold_said)); hold_said.proto = IPPROTO_INT; hold_said.spi = htonl(SPI_HOLD); hold_said.dst.s_addr = INADDR_ANY; hold_eroute.er_eaddr.sen_len = sizeof(struct sockaddr_encap); hold_eroute.er_emask.sen_len = sizeof(struct sockaddr_encap); hold_eroute.er_eaddr.sen_family = AF_ENCAP; hold_eroute.er_emask.sen_family = AF_ENCAP; hold_eroute.er_eaddr.sen_type = SENT_IP4; hold_eroute.er_emask.sen_type = 255; hold_eroute.er_eaddr.sen_ip_src.s_addr = iph->saddr; hold_eroute.er_eaddr.sen_ip_dst.s_addr = iph->daddr; hold_eroute.er_emask.sen_ip_src.s_addr = INADDR_BROADCAST; hold_eroute.er_emask.sen_ip_dst.s_addr = INADDR_BROADCAST; hold_eroute.er_emask.sen_sport = ~0; hold_eroute.er_emask.sen_dport = ~0; hold_eroute.er_pid = eroute_pid; hold_eroute.er_count = 0; hold_eroute.er_lasttime = jiffies/HZ; hold_eroute.er_eaddr.sen_proto = iph->protocol; ipsec_extract_ports(iph, &hold_eroute.er_eaddr); #ifdef CONFIG_IPSEC_DEBUG if (debug_pfkey) { char buf1[64], buf2[64]; subnettoa(hold_eroute.er_eaddr.sen_ip_src, hold_eroute.er_emask.sen_ip_src, 0, buf1, sizeof(buf1)); subnettoa(hold_eroute.er_eaddr.sen_ip_dst, hold_eroute.er_emask.sen_ip_dst, 0, buf2, sizeof(buf2)); KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "calling breakeroute and makeroute for %s:%d->%s:%d %d HOLD eroute.\n", buf1, ntohs(hold_eroute.er_eaddr.sen_sport), buf2, ntohs(hold_eroute.er_eaddr.sen_dport), hold_eroute.er_eaddr.sen_proto); } #endif /* CONFIG_IPSEC_DEBUG */ if (ipsec_breakroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask), &first, &last)) { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD breakeroute found nothing.\n"); } else { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD breakroute deleted %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u %u\n", NIPQUAD(hold_eroute.er_eaddr.sen_ip_src), ntohs(hold_eroute.er_eaddr.sen_sport), NIPQUAD(hold_eroute.er_eaddr.sen_ip_dst), ntohs(hold_eroute.er_eaddr.sen_dport), hold_eroute.er_eaddr.sen_proto); } if (first != NULL) kfree_skb(first); if (last != NULL) kfree_skb(last); error = ipsec_makeroute(&(hold_eroute.er_eaddr), &(hold_eroute.er_emask), hold_said, eroute_pid, skb, NULL, NULL); if (error) { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD makeroute returned %d, failed.\n", error); } else { KLIPS_PRINT(debug_pfkey, "klips_debug:ipsec_tunnel_start_xmit: " "HOLD makeroute call successful.\n"); } return (error == 0); } #ifdef CONFIG_IPSEC_DEBUG_ DEBUG_NO_STATIC void dmp(char *s, caddr_t bb, int len) { int i; unsigned char *b = bb; if (debug_tunnel) { printk(KERN_INFO "klips_debug:ipsec_tunnel_:dmp: " "at %s, len=%d:", s, len); for (i=0; i < len; i++) { if(!(i%16)){ printk("\nklips_debug: "); } printk(" %02x", *b++); } printk("\n"); } } #else /* CONFIG_IPSEC_DEBUG */ #define dmp(_x, _y, _z) #endif /* CONFIG_IPSEC_DEBUG */ #ifndef SKB_COPY_EXPAND /* * This is mostly skbuff.c:skb_copy(). */ struct sk_buff * skb_copy_expand(struct sk_buff *skb, int headroom, int tailroom, int priority) { struct sk_buff *n; unsigned long offset; /* * Do sanity checking */ if((headroom < 0) || (tailroom < 0) || ((headroom+tailroom) < 0)) { printk(KERN_WARNING "klips_error:skb_copy_expand: " "Illegal negative head,tailroom %d,%d\n", headroom, tailroom); return NULL; } /* * Allocate the copy buffer */ #ifndef NET_21 IS_SKB(skb); #endif /* !NET_21 */ n=alloc_skb(skb->end - skb->head + headroom + tailroom, priority); KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:skb_copy_expand: " "allocating %d bytes, head=0p%p data=0p%p tail=0p%p end=0p%p end-head=%d tail-data=%d\n", skb->end - skb->head + headroom + tailroom, skb->head, skb->data, skb->tail, skb->end, skb->end - skb->head, skb->tail - skb->data); if(n==NULL) return NULL; /* * Shift between the two data areas in bytes */ /* Set the data pointer */ skb_reserve(n,skb->data-skb->head+headroom); /* Set the tail pointer and length */ if(skb_tailroom(n) < skb->len) { printk(KERN_WARNING "klips_error:skb_copy_expand: " "tried to skb_put %ld, %d available. This should never happen, please report.\n", (unsigned long int)skb->len, skb_tailroom(n)); ipsec_kfree_skb(n); return NULL; } skb_put(n,skb->len); offset=n->head + headroom - skb->head; /* Copy the bytes */ memcpy(n->head + headroom, skb->head,skb->end-skb->head); #ifdef NET_21 n->csum=skb->csum; n->priority=skb->priority; n->dst=dst_clone(skb->dst); if(skb->nh.raw) n->nh.raw=skb->nh.raw+offset; #ifndef NETDEV_23 n->is_clone=0; #endif /* NETDEV_23 */ atomic_set(&n->users, 1); n->destructor = NULL; n->security=skb->security; #else /* NET_21 */ n->link3=NULL; n->when=skb->when; if(skb->ip_hdr) n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset); n->saddr=skb->saddr; n->daddr=skb->daddr; n->raddr=skb->raddr; n->seq=skb->seq; n->end_seq=skb->end_seq; n->ack_seq=skb->ack_seq; n->acked=skb->acked; n->free=1; n->arp=skb->arp; n->tries=0; n->lock=0; n->users=0; #endif /* NET_21 */ n->protocol=skb->protocol; n->list=NULL; n->sk=NULL; n->dev=skb->dev; if(skb->h.raw) n->h.raw=skb->h.raw+offset; if(skb->mac.raw) n->mac.raw=skb->mac.raw+offset; memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv)); #ifndef NETDEV_23 n->used=skb->used; #endif /* !NETDEV_23 */ n->pkt_type=skb->pkt_type; n->stamp=skb->stamp; #ifndef NET_21 IS_SKB(n); #endif /* !NET_21 */ return n; } #endif /* !SKB_COPY_EXPAND */ #ifdef CONFIG_IPSEC_DEBUG void ipsec_print_ip(struct iphdr *ip) { char buf[ADDRTOA_BUF]; printk(KERN_INFO "klips_debug: IP:"); printk(" ihl:%d", ip->ihl << 2); printk(" ver:%d", ip->version); printk(" tos:%d", ip->tos); printk(" tlen:%d", ntohs(ip->tot_len)); printk(" id:%d", ntohs(ip->id)); printk(" %s%s%sfrag_off:%d", ip->frag_off & __constant_htons(IP_CE) ? "CE " : "", ip->frag_off & __constant_htons(IP_DF) ? "DF " : "", ip->frag_off & __constant_htons(IP_MF) ? "MF " : "", (ntohs(ip->frag_off) & IP_OFFSET) << 3); printk(" ttl:%d", ip->ttl); printk(" proto:%d", ip->protocol); if(ip->protocol == IPPROTO_UDP) printk(" (UDP)"); if(ip->protocol == IPPROTO_TCP) printk(" (TCP)"); if(ip->protocol == IPPROTO_ICMP) printk(" (ICMP)"); printk(" chk:%d", ntohs(ip->check)); addrtoa(*((struct in_addr*)(&ip->saddr)), 0, buf, sizeof(buf)); printk(" saddr:%s", buf); if(ip->protocol == IPPROTO_UDP) printk(":%d", ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); if(ip->protocol == IPPROTO_TCP) printk(":%d", ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->source)); addrtoa(*((struct in_addr*)(&ip->daddr)), 0, buf, sizeof(buf)); printk(" daddr:%s", buf); if(ip->protocol == IPPROTO_UDP) printk(":%d", ntohs(((struct udphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); if(ip->protocol == IPPROTO_TCP) printk(":%d", ntohs(((struct tcphdr*)((caddr_t)ip + (ip->ihl << 2)))->dest)); if(ip->protocol == IPPROTO_ICMP) printk(" type:code=%d:%d", ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->type, ((struct icmphdr*)((caddr_t)ip + (ip->ihl << 2)))->code); printk("\n"); if(sysctl_ipsec_debug_verbose) { __u8 *c; int i; c = ((__u8*)ip) + ip->ihl*4; for(i = 0; i < ntohs(ip->tot_len) - ip->ihl*4; i++ /*, c++*/) { if(!(i % 16)) { printk(KERN_INFO "klips_debug: @%03x:", i); } printk(" %02x", /***/c[i]); if(!((i + 1) % 16)) { printk("\n"); } } if(i % 16) { printk("\n"); } } } #endif /* CONFIG_IPSEC_DEBUG */ #ifdef MSS_HACK /* * Issues: * 1) Fragments arriving in the tunnel should probably be rejected. * 2) How does this affect syncookies, mss_cache, dst cache ? * 3) Path MTU discovery handling needs to be reviewed. For example, * if we receive an ICMP 'packet too big' message from an intermediate * router specifying it's next hop MTU, our stack may process this and * adjust the MSS without taking our AH/ESP overheads into account. */ /* * Recaclulate checksum using differences between changed datum, * borrowed from netfilter. */ DEBUG_NO_STATIC u_int16_t ipsec_fast_csum(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) { u_int32_t diffs[] = { oldvalinv, newval }; return csum_fold(csum_partial((char *)diffs, sizeof(diffs), oldcheck^0xFFFF)); } /* * Determine effective MSS. * * Note that we assume that there is always an MSS option for our own * SYN segments, which is mentioned in tcp_syn_build_options(), kernel 2.2.x. * This could change, and we should probably parse TCP options instead. * */ DEBUG_NO_STATIC u_int8_t ipsec_adjust_mss(struct sk_buff *skb, struct tcphdr *tcph, u_int16_t mtu) { u_int16_t oldmss, newmss; u_int32_t *mssp; struct sock *sk = skb->sk; newmss = tcp_sync_mss(sk, mtu); printk(KERN_INFO "klips: setting mss to %u\n", newmss); mssp = (u_int32_t *)tcph + sizeof(struct tcphdr) / sizeof(u_int32_t); oldmss = ntohl(*mssp) & 0x0000FFFF; *mssp = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | newmss); tcph->check = ipsec_fast_csum(htons(~oldmss), htons(newmss), tcph->check); return 1; } #endif /* MSS_HACK */ /* * Sanity checks */ enum ipsec_xmit_value ipsec_xmit_sanity_check_dev(struct ipsec_xmit_state *ixs) { if (ixs->dev == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_xmit_sanity_check_dev: " "No device associated with skb!\n" ); return IPSEC_XMIT_NODEV; } ixs->prv = ixs->dev->priv; if (ixs->prv == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_xmit_sanity_check_dev: " "Device has no private structure!\n" ); return IPSEC_XMIT_NOPRIVDEV; } ixs->physdev = ixs->prv->dev; if (ixs->physdev == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_xmit_sanity_check_dev: " "Device is not attached to physical device!\n" ); return IPSEC_XMIT_NOPHYSDEV; } ixs->physmtu = ixs->physdev->mtu; ixs->stats = (struct net_device_stats *) &(ixs->prv->mystats); return IPSEC_XMIT_OK; } enum ipsec_xmit_value ipsec_xmit_sanity_check_skb(struct ipsec_xmit_state *ixs) { /* * Return if there is nothing to do. (Does this ever happen?) XXX */ if (ixs->skb == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_xmit_sanity_check_skb: " "Nothing to do!\n" ); return IPSEC_XMIT_NOSKB; } #ifdef NET_21 /* if skb was cloned (most likely due to a packet sniffer such as tcpdump being momentarily attached to the interface), make a copy of our own to modify */ if(skb_cloned(ixs->skb)) { if #ifdef SKB_COW_NEW (skb_cow(ixs->skb, skb_headroom(ixs->skb)) != 0) #else /* SKB_COW_NEW */ ((ixs->skb = skb_cow(ixs->skb, skb_headroom(ixs->skb))) == NULL) #endif /* SKB_COW_NEW */ { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_error:ipsec_xmit_sanity_check_skb: " "skb_cow failed to allocate buffer, dropping.\n" ); ixs->stats->tx_dropped++; return IPSEC_XMIT_ERRSKBALLOC; } } #endif /* NET_21 */ #ifdef NET_21 ixs->iph = ixs->skb->nh.iph; #else /* NET_21 */ ixs->iph = ixs->skb->ip_hdr; #endif /* NET_21 */ /* sanity check for IP version as we can't handle IPv6 right now */ if (ixs->iph->version != 4) { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: " "found IP Version %d but cannot process other IP versions than v4.\n", ixs->iph->version); /* XXX */ ixs->stats->tx_dropped++; return IPSEC_XMIT_NOIPV6; } #if IPSEC_DISALLOW_IPOPTIONS if ((ixs->iph->ihl << 2) != sizeof (struct iphdr)) { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: " "cannot process IP header options yet. May be mal-formed packet.\n"); /* XXX */ ixs->stats->tx_dropped++; return IPSEC_XMIT_NOIPOPTIONS; } #endif /* IPSEC_DISALLOW_IPOPTIONS */ #ifndef NET_21 if (ixs->iph->ttl <= 0) { /* Tell the sender its packet died... */ ICMP_SEND(ixs->skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, ixs->physdev); KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_xmit_sanity_check_skb: " "TTL=0, too many hops!\n"); ixs->stats->tx_dropped++; return IPSEC_XMIT_TTLEXPIRED; } #endif /* !NET_21 */ return IPSEC_XMIT_OK; } enum ipsec_xmit_value ipsec_xmit_encap_once(struct ipsec_xmit_state *ixs) { #ifdef CONFIG_IPSEC_ESP struct esphdr *espp; #ifdef CONFIG_IPSEC_ENC_3DES __u32 iv[ESP_IV_MAXSZ_INT]; #endif /* !CONFIG_IPSEC_ENC_3DES */ unsigned char *idat, *pad; int authlen = 0, padlen = 0, i; #endif /* !CONFIG_IPSEC_ESP */ #ifdef CONFIG_IPSEC_AH struct iphdr ipo; struct ahhdr *ahp; #endif /* CONFIG_IPSEC_AH */ #if defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) union { #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 MD5_CTX md5; #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ #ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 SHA1_CTX sha1; #endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ } tctx; __u8 hash[AH_AMAX]; #endif /* defined(CONFIG_IPSEC_AUTH_HMAC_MD5) || defined(CONFIG_IPSEC_AUTH_HMAC_SHA1) */ int headroom = 0, tailroom = 0, ilen = 0, len = 0; unsigned char *dat; int blocksize = 8; /* XXX: should be inside ixs --jjo */ #ifdef CONFIG_IPSEC_ALG struct ipsec_alg_enc *ixt_e = NULL; struct ipsec_alg_auth *ixt_a = NULL; #endif /* CONFIG_IPSEC_ALG */ ixs->iphlen = ixs->iph->ihl << 2; ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; ixs->sa_len = satoa(ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOA_BUF); KLIPS_PRINT(debug_tunnel & DB_TN_OXFS, "klips_debug:ipsec_xmit_encap_once: " "calling output for <%s%s%s>, SA:%s\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); switch(ixs->ipsp->ips_said.proto) { #ifdef CONFIG_IPSEC_AH case IPPROTO_AH: headroom += sizeof(struct ahhdr); break; #endif /* CONFIG_IPSEC_AH */ #ifdef CONFIG_IPSEC_ESP case IPPROTO_ESP: #ifdef CONFIG_IPSEC_ALG if ((ixt_e=ixs->ipsp->ips_alg_enc)) { blocksize = ixt_e->ixt_blocksize; headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_encalg) { #ifdef CONFIG_IPSEC_ENC_3DES case ESP_3DES: headroom += sizeof(struct esphdr); break; #endif /* CONFIG_IPSEC_ENC_3DES */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_BADALG; } #ifdef CONFIG_IPSEC_ALG if ((ixt_a=ixs->ipsp->ips_alg_auth)) { tailroom += AHHMAC_HASHLEN; } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_authalg) { #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 case AH_MD5: authlen = AHHMAC_HASHLEN; break; #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ #ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 case AH_SHA: authlen = AHHMAC_HASHLEN; break; #endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ case AH_NONE: break; default: ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_BADALG; } #ifdef CONFIG_IPSEC_ALG tailroom += blocksize != 1 ? ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; #else tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; #endif /* CONFIG_IPSEC_ALG */ tailroom += authlen; break; #endif /* !CONFIG_IPSEC_ESP */ #ifdef CONFIG_IPSEC_IPIP case IPPROTO_IPIP: headroom += sizeof(struct iphdr); ixs->iphlen = sizeof(struct iphdr); break; #endif /* !CONFIG_IPSEC_IPIP */ #ifdef CONFIG_IPSEC_IPCOMP case IPPROTO_COMP: break; #endif /* CONFIG_IPSEC_IPCOMP */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_BADPROTO; } KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_once: " "pushing %d bytes, putting %d, proto %d.\n", headroom, tailroom, ixs->ipsp->ips_said.proto); if(skb_headroom(ixs->skb) < headroom) { printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: " "tried to skb_push headroom=%d, %d available. This should never happen, please report.\n", headroom, skb_headroom(ixs->skb)); ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_PUSHPULLERR; } dat = skb_push(ixs->skb, headroom); ilen = ixs->skb->len - tailroom; if(skb_tailroom(ixs->skb) < tailroom) { printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: " "tried to skb_put %d, %d available. This should never happen, please report.\n", tailroom, skb_tailroom(ixs->skb)); ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_PUSHPULLERR; } skb_put(ixs->skb, tailroom); KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_once: " "head,tailroom: %d,%d before xform.\n", skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); len = ixs->skb->len; if(len > 0xfff0) { printk(KERN_WARNING "klips_error:ipsec_xmit_encap_once: " "tot_len (%d) > 65520. This should never happen, please report.\n", len); ixs->stats->tx_errors++; return IPSEC_XMIT_BADLEN; } memmove((void *)dat, (void *)(dat + headroom), ixs->iphlen); ixs->iph = (struct iphdr *)dat; ixs->iph->tot_len = htons(ixs->skb->len); switch(ixs->ipsp->ips_said.proto) { #ifdef CONFIG_IPSEC_ESP case IPPROTO_ESP: espp = (struct esphdr *)(dat + ixs->iphlen); espp->esp_spi = ixs->ipsp->ips_said.spi; espp->esp_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); #ifdef CONFIG_IPSEC_ALG if (!ixt_e) #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_encalg) { #if defined(CONFIG_IPSEC_ENC_3DES) #ifdef CONFIG_IPSEC_ENC_3DES case ESP_3DES: #endif /* CONFIG_IPSEC_ENC_3DES */ iv[0] = *((__u32*)&(espp->esp_iv) ) = ((__u32*)(ixs->ipsp->ips_iv))[0]; iv[1] = *((__u32*)&(espp->esp_iv) + 1) = ((__u32*)(ixs->ipsp->ips_iv))[1]; break; #endif /* defined(CONFIG_IPSEC_ENC_3DES) */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_BADALG; } idat = dat + ixs->iphlen + headroom; ilen = len - (ixs->iphlen + headroom + authlen); /* Self-describing padding */ pad = &dat[len - tailroom]; padlen = tailroom - 2 - authlen; for (i = 0; i < padlen; i++) { pad[i] = i + 1; } dat[len - authlen - 2] = padlen; dat[len - authlen - 1] = ixs->iph->protocol; ixs->iph->protocol = IPPROTO_ESP; #ifdef CONFIG_IPSEC_ALG /* Do all operations here: * copy IV->ESP, encrypt, update ips IV */ if (ixt_e) { int ret; memcpy(espp->esp_iv, ixs->ipsp->ips_iv, ixt_e->ixt_ivlen/8); ret=ipsec_alg_esp_encrypt(ixs->ipsp, idat, ilen, espp->esp_iv, IPSEC_ALG_ENCRYPT); memcpy(ixs->ipsp->ips_iv, idat + ilen - ixt_e->ixt_ivlen/8, ixt_e->ixt_ivlen/8); } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_encalg) { #ifdef CONFIG_IPSEC_ENC_3DES case ESP_3DES: des_ede3_cbc_encrypt((des_cblock *)idat, (des_cblock *)idat, ilen, ((struct des_eks *)(ixs->ipsp->ips_key_e))[0].ks, ((struct des_eks *)(ixs->ipsp->ips_key_e))[1].ks, ((struct des_eks *)(ixs->ipsp->ips_key_e))[2].ks, (des_cblock *)iv, 1); break; #endif /* CONFIG_IPSEC_ENC_3DES */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_BADALG; } #ifdef CONFIG_IPSEC_ALG if (!ixt_e) #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_encalg) { #if defined(CONFIG_IPSEC_ENC_3DES) #ifdef CONFIG_IPSEC_ENC_3DES case ESP_3DES: #endif /* CONFIG_IPSEC_ENC_3DES */ /* XXX update IV with the last 8 octets of the encryption */ #if KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK ((__u32*)(ixs->ipsp->ips_iv))[0] = ((__u32 *)(idat))[(ilen >> 2) - 2]; ((__u32*)(ixs->ipsp->ips_iv))[1] = ((__u32 *)(idat))[(ilen >> 2) - 1]; #else /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ prng_bytes(&ipsec_prng, (char *)ixs->ipsp->ips_iv, EMT_ESPDES_IV_SZ); #endif /* KLIPS_IMPAIRMENT_ESPIV_CBC_ATTACK */ break; #endif /* defined(CONFIG_IPSEC_ENC_3DES) */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_ESP_BADALG; } #ifdef CONFIG_IPSEC_ALG if (ixt_a) { ipsec_alg_sa_esp_hash(ixs->ipsp, (caddr_t)espp, len - ixs->iphlen - authlen, &(dat[len - authlen]), authlen); } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_authalg) { #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 case AH_MD5: dmp("espp", (char*)espp, len - ixs->iphlen - authlen); tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, (caddr_t)espp, len - ixs->iphlen - authlen); dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Final(hash, &tctx.md5); dmp("ictx hash", (char*)&hash, sizeof(hash)); tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, hash, AHMD596_ALEN); dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Final(hash, &tctx.md5); dmp("octx hash", (char*)&hash, sizeof(hash)); memcpy(&(dat[len - authlen]), hash, authlen); /* paranoid */ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); memset((caddr_t)hash, 0, sizeof(*hash)); break; #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ #ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 case AH_SHA: tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; SHA1Update(&tctx.sha1, (caddr_t)espp, len - ixs->iphlen - authlen); SHA1Final(hash, &tctx.sha1); tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); SHA1Final(hash, &tctx.sha1); memcpy(&(dat[len - authlen]), hash, authlen); /* paranoid */ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); memset((caddr_t)hash, 0, sizeof(*hash)); break; #endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ case AH_NONE: break; default: ixs->stats->tx_errors++; return IPSEC_XMIT_AH_BADALG; } #ifdef NET_21 ixs->skb->h.raw = (unsigned char*)espp; #endif /* NET_21 */ break; #endif /* !CONFIG_IPSEC_ESP */ #ifdef CONFIG_IPSEC_AH case IPPROTO_AH: ahp = (struct ahhdr *)(dat + ixs->iphlen); ahp->ah_spi = ixs->ipsp->ips_said.spi; ahp->ah_rpl = htonl(++(ixs->ipsp->ips_replaywin_lastseq)); ahp->ah_rv = 0; ahp->ah_nh = ixs->iph->protocol; ahp->ah_hl = (headroom >> 2) - sizeof(__u64)/sizeof(__u32); ixs->iph->protocol = IPPROTO_AH; dmp("ahp", (char*)ahp, sizeof(*ahp)); ipo = *ixs->iph; ipo.tos = 0; ipo.frag_off = 0; ipo.ttl = 0; ipo.check = 0; dmp("ipo", (char*)&ipo, sizeof(ipo)); switch(ixs->ipsp->ips_authalg) { #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 case AH_MD5: tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->ictx; dmp("ictx", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, (unsigned char *)&ipo, sizeof (struct iphdr)); dmp("ictx+ipo", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); dmp("ictx+ahp", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, (unsigned char *)zeroes, AHHMAC_HASHLEN); dmp("ictx+zeroes", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); dmp("ictx+dat", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Final(hash, &tctx.md5); dmp("ictx hash", (char*)&hash, sizeof(hash)); tctx.md5 = ((struct md5_ctx*)(ixs->ipsp->ips_key_a))->octx; dmp("octx", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Update(&tctx.md5, hash, AHMD596_ALEN); dmp("octx+hash", (char*)&tctx.md5, sizeof(tctx.md5)); MD5Final(hash, &tctx.md5); dmp("octx hash", (char*)&hash, sizeof(hash)); memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); /* paranoid */ memset((caddr_t)&tctx.md5, 0, sizeof(tctx.md5)); memset((caddr_t)hash, 0, sizeof(*hash)); break; #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ #ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 case AH_SHA: tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->ictx; SHA1Update(&tctx.sha1, (unsigned char *)&ipo, sizeof (struct iphdr)); SHA1Update(&tctx.sha1, (unsigned char *)ahp, headroom - sizeof(ahp->ah_data)); SHA1Update(&tctx.sha1, (unsigned char *)zeroes, AHHMAC_HASHLEN); SHA1Update(&tctx.sha1, dat + ixs->iphlen + headroom, len - ixs->iphlen - headroom); SHA1Final(hash, &tctx.sha1); tctx.sha1 = ((struct sha1_ctx*)(ixs->ipsp->ips_key_a))->octx; SHA1Update(&tctx.sha1, hash, AHSHA196_ALEN); SHA1Final(hash, &tctx.sha1); memcpy(ahp->ah_data, hash, AHHMAC_HASHLEN); /* paranoid */ memset((caddr_t)&tctx.sha1, 0, sizeof(tctx.sha1)); memset((caddr_t)hash, 0, sizeof(*hash)); break; #endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_AH_BADALG; } #ifdef NET_21 ixs->skb->h.raw = (unsigned char*)ahp; #endif /* NET_21 */ break; #endif /* CONFIG_IPSEC_AH */ #ifdef CONFIG_IPSEC_IPIP case IPPROTO_IPIP: ixs->iph->version = 4; switch(sysctl_ipsec_tos) { case 0: #ifdef NET_21 ixs->iph->tos = ixs->skb->nh.iph->tos; #else /* NET_21 */ ixs->iph->tos = ixs->skb->ip_hdr->tos; #endif /* NET_21 */ break; case 1: ixs->iph->tos = 0; break; default: break; } #ifdef NET_21 #ifdef NETDEV_23 ixs->iph->ttl = sysctl_ip_default_ttl; #else /* NETDEV_23 */ ixs->iph->ttl = ip_statistics.IpDefaultTTL; #endif /* NETDEV_23 */ #else /* NET_21 */ ixs->iph->ttl = 64; /* ip_statistics.IpDefaultTTL; */ #endif /* NET_21 */ ixs->iph->frag_off = 0; ixs->iph->saddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_s))->sin_addr.s_addr; ixs->iph->daddr = ((struct sockaddr_in*)(ixs->ipsp->ips_addr_d))->sin_addr.s_addr; ixs->iph->protocol = IPPROTO_IPIP; ixs->iph->ihl = sizeof(struct iphdr) >> 2; KLIPS_IP_SELECT_IDENT(ixs->iph, ixs->skb); ixs->newdst = (__u32)ixs->iph->daddr; ixs->newsrc = (__u32)ixs->iph->saddr; #ifdef NET_21 ixs->skb->h.ipiph = ixs->skb->nh.iph; #endif /* NET_21 */ break; #endif /* !CONFIG_IPSEC_IPIP */ #ifdef CONFIG_IPSEC_IPCOMP case IPPROTO_COMP: { unsigned int flags = 0; #ifdef CONFIG_IPSEC_DEBUG unsigned int old_tot_len = ntohs(ixs->iph->tot_len); #endif /* CONFIG_IPSEC_DEBUG */ ixs->ipsp->ips_comp_ratio_dbytes += ntohs(ixs->iph->tot_len); ixs->skb = skb_compress(ixs->skb, ixs->ipsp, &flags); #ifdef NET_21 ixs->iph = ixs->skb->nh.iph; #else /* NET_21 */ ixs->iph = ixs->skb->ip_hdr; #endif /* NET_21 */ ixs->ipsp->ips_comp_ratio_cbytes += ntohs(ixs->iph->tot_len); #ifdef CONFIG_IPSEC_DEBUG if (debug_tunnel & DB_TN_CROUT) { if (old_tot_len > ntohs(ixs->iph->tot_len)) KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_once: " "packet shrunk from %d to %d bytes after compression, cpi=%04x (should be from spi=%08x, spi&0xffff=%04x.\n", old_tot_len, ntohs(ixs->iph->tot_len), ntohs(((struct ipcomphdr*)(((char*)ixs->iph) + ((ixs->iph->ihl) << 2)))->ipcomp_cpi), ntohl(ixs->ipsp->ips_said.spi), (__u16)(ntohl(ixs->ipsp->ips_said.spi) & 0x0000ffff)); else KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_once: " "packet did not compress (flags = %d).\n", flags); } #endif /* CONFIG_IPSEC_DEBUG */ } break; #endif /* CONFIG_IPSEC_IPCOMP */ default: ixs->stats->tx_errors++; return IPSEC_XMIT_BADPROTO; } #ifdef NET_21 ixs->skb->nh.raw = ixs->skb->data; #else /* NET_21 */ ixs->skb->ip_hdr = ixs->skb->h.iph = (struct iphdr *) ixs->skb->data; #endif /* NET_21 */ ixs->iph->check = 0; ixs->iph->check = ip_fast_csum((unsigned char *)ixs->iph, ixs->iph->ihl); KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_once: " "after <%s%s%s>, SA:%s:\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); KLIPS_IP_PRINT(debug_tunnel & DB_TN_XMIT, ixs->iph); ixs->ipsp->ips_life.ipl_bytes.ipl_count += len; ixs->ipsp->ips_life.ipl_bytes.ipl_last = len; if(!ixs->ipsp->ips_life.ipl_usetime.ipl_count) { ixs->ipsp->ips_life.ipl_usetime.ipl_count = jiffies / HZ; } ixs->ipsp->ips_life.ipl_usetime.ipl_last = jiffies / HZ; ixs->ipsp->ips_life.ipl_packets.ipl_count++; ixs->ipsp = ixs->ipsp->ips_onext; return IPSEC_XMIT_OK; } enum ipsec_xmit_value ipsec_xmit_encap_bundle(struct ipsec_xmit_state *ixs) { #ifdef CONFIG_IPSEC_ALG struct ipsec_alg_enc *ixt_e = NULL; struct ipsec_alg_auth *ixt_a = NULL; int blocksize = 8; #endif /* CONFIG_IPSEC_ALG */ enum ipsec_xmit_value bundle_stat = IPSEC_XMIT_OK; ixs->newdst = ixs->orgdst = ixs->iph->daddr; ixs->newsrc = ixs->orgsrc = ixs->iph->saddr; ixs->orgedst = ixs->outgoing_said.dst.s_addr; ixs->iphlen = ixs->iph->ihl << 2; ixs->pyldsz = ntohs(ixs->iph->tot_len) - ixs->iphlen; ixs->max_headroom = ixs->max_tailroom = 0; if (ixs->outgoing_said.proto == IPPROTO_INT) { switch (ntohl(ixs->outgoing_said.spi)) { case SPI_DROP: KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "shunt SA of DROP or no eroute: dropping.\n"); ixs->stats->tx_dropped++; break; case SPI_REJECT: KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "shunt SA of REJECT: notifying and dropping.\n"); ICMP_SEND(ixs->skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0, ixs->physdev); ixs->stats->tx_dropped++; break; case SPI_PASS: #ifdef NET_21 ixs->pass = 1; #endif /* NET_21 */ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "PASS: calling dev_queue_xmit\n"); return IPSEC_XMIT_PASS; goto cleanup; #if 1 /* now moved up to finderoute so we don't need to lock it longer */ case SPI_HOLD: KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "shunt SA of HOLD: this does not make sense here, dropping.\n"); ixs->stats->tx_dropped++; break; #endif case SPI_TRAP: case SPI_TRAPSUBNET: { struct sockaddr_in src, dst; #ifdef CONFIG_IPSEC_DEBUG char bufsrc[ADDRTOA_BUF], bufdst[ADDRTOA_BUF]; #endif /* CONFIG_IPSEC_DEBUG */ /* Signal all listening KMds with a PF_KEY ACQUIRE */ ixs->ips.ips_said.proto = ixs->iph->protocol; src.sin_family = AF_INET; dst.sin_family = AF_INET; src.sin_addr.s_addr = ixs->iph->saddr; dst.sin_addr.s_addr = ixs->iph->daddr; src.sin_port = (ixs->iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->source : (ixs->iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->source : 0)); dst.sin_port = (ixs->iph->protocol == IPPROTO_UDP ? ((struct udphdr*) (((caddr_t)ixs->iph) + (ixs->iph->ihl << 2)))->dest : (ixs->iph->protocol == IPPROTO_TCP ? ((struct tcphdr*)((caddr_t)ixs->iph + (ixs->iph->ihl << 2)))->dest : 0)); { int i; for(i = 0; i < sizeof(struct sockaddr_in) - offsetof(struct sockaddr_in, sin_zero); i++) { src.sin_zero[i] = 0; dst.sin_zero[i] = 0; } } ixs->ips.ips_addr_s = (struct sockaddr*)(&src); ixs->ips.ips_addr_d = (struct sockaddr*)(&dst); KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "SADB_ACQUIRE sent with src=%s:%d, dst=%s:%d, proto=%d.\n", addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_addr, 0, bufsrc, sizeof(bufsrc)) <= ADDRTOA_BUF ? bufsrc : "BAD_ADDR", ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_s))->sin_port), addrtoa(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_addr, 0, bufdst, sizeof(bufdst)) <= ADDRTOA_BUF ? bufdst : "BAD_ADDR", ntohs(((struct sockaddr_in*)(ixs->ips.ips_addr_d))->sin_port), ixs->ips.ips_said.proto); /* increment count of total traps needed */ ipsec_xmit_trap_count++; if (pfkey_acquire(&ixs->ips) == 0) { /* note that we succeeded */ ipsec_xmit_trap_sendcount++; if (ixs->outgoing_said.spi==htonl(SPI_TRAPSUBNET)) { /* * The spinlock is to prevent any other * process from accessing or deleting * the eroute while we are using and * updating it. */ spin_lock(&eroute_lock); ixs->eroute = ipsec_findroute(&ixs->matcher); if(ixs->eroute) { ixs->eroute->er_said.spi = htonl(SPI_HOLD); ixs->eroute->er_first = ixs->skb; ixs->skb = NULL; } spin_unlock(&eroute_lock); } else if (create_hold_eroute(ixs->skb, ixs->iph, ixs->eroute_pid)) { ixs->skb = NULL; } } ixs->stats->tx_dropped++; } default: /* XXX what do we do with an unknown shunt spi? */ break; } /* switch (ntohl(ixs->outgoing_said.spi)) */ return IPSEC_XMIT_STOLEN; } /* if (ixs->outgoing_said.proto == IPPROTO_INT) */ /* The spinlock is to prevent any other process from accessing or deleting the ipsec_sa hash table or any of the ipsec_sa s while we are using and updating them. This is not optimal, but was relatively straightforward at the time. A better way to do it has been planned for more than a year, to lock the hash table and put reference counts on each ipsec_sa instead. This is not likely to happen in KLIPS1 unless a volunteer contributes it, but will be designed into KLIPS2. */ spin_lock(&tdb_lock); ixs->ipsp = ipsec_sa_getbyid(&ixs->outgoing_said); ixs->sa_len = satoa(ixs->outgoing_said, 0, ixs->sa_txt, SATOA_BUF); if (ixs->ipsp == NULL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "no ipsec_sa for SA%s: outgoing packet with no SA, dropped.\n", ixs->sa_len ? ixs->sa_txt : " (error)"); ixs->stats->tx_dropped++; bundle_stat = IPSEC_XMIT_SAIDNOTFOUND; goto cleanup; } ipsec_sa_put(ixs->ipsp); /* incomplete */ KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "found ipsec_sa -- SA:<%s%s%s> %s\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); /* * How much headroom do we need to be able to apply * all the grouped transforms? */ ixs->ipsq = ixs->ipsp; /* save the head of the ipsec_sa chain */ while (ixs->ipsp) { ixs->sa_len = satoa(ixs->ipsp->ips_said, 0, ixs->sa_txt, SATOA_BUF); if(ixs->sa_len == 0) { strcpy(ixs->sa_txt, "(error)"); } /* If it is in larval state, drop the packet, we cannot process yet. */ if(ixs->ipsp->ips_state == SADB_SASTATE_LARVAL) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "ipsec_sa in larval state for SA:<%s%s%s> %s, cannot be used yet, dropping packet.\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; goto cleanup; } if(ixs->ipsp->ips_state == SADB_SASTATE_DEAD) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "ipsec_sa in dead state for SA:<%s%s%s> %s, can no longer be used, dropping packet.\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_SAIDNOTLIVE; goto cleanup; } /* If the replay window counter == -1, expire SA, it will roll */ if(ixs->ipsp->ips_replaywin && ixs->ipsp->ips_replaywin_lastseq == -1) { pfkey_expire(ixs->ipsp, 1); KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "replay window counter rolled for SA:<%s%s%s> %s, packet dropped, expiring SA.\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); ipsec_sa_delchain(ixs->ipsp); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_REPLAYROLLED; goto cleanup; } /* * if this is the first time we are using this SA, mark start time, * and offset hard/soft counters by "now" for later checking. */ #if 0 if(ixs->ipsp->ips_life.ipl_usetime.count == 0) { ixs->ipsp->ips_life.ipl_usetime.count = jiffies; ixs->ipsp->ips_life.ipl_usetime.hard += jiffies; ixs->ipsp->ips_life.ipl_usetime.soft += jiffies; } #endif if(ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_bytes, "bytes", ixs->sa_txt, ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_addtime, "addtime",ixs->sa_txt, ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_usetime, "usetime",ixs->sa_txt, ipsec_life_timebased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied || ipsec_lifetime_check(&ixs->ipsp->ips_life.ipl_packets, "packets",ixs->sa_txt, ipsec_life_countbased, ipsec_outgoing, ixs->ipsp) == ipsec_life_harddied) { ipsec_sa_delchain(ixs->ipsp); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_LIFETIMEFAILED; goto cleanup; } ixs->headroom = ixs->tailroom = 0; KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "calling room for <%s%s%s>, SA:%s\n", IPS_XFORM_NAME(ixs->ipsp), ixs->sa_len ? ixs->sa_txt : " (error)"); switch(ixs->ipsp->ips_said.proto) { #ifdef CONFIG_IPSEC_AH case IPPROTO_AH: ixs->headroom += sizeof(struct ahhdr); break; #endif /* CONFIG_IPSEC_AH */ #ifdef CONFIG_IPSEC_ESP case IPPROTO_ESP: #ifdef CONFIG_IPSEC_ALG if ((ixt_e=ixs->ipsp->ips_alg_enc)) { blocksize = ixt_e->ixt_blocksize; ixs->headroom += ESP_HEADER_LEN + ixt_e->ixt_ivlen/8; } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_encalg) { #ifdef CONFIG_IPSEC_ENC_3DES case ESP_3DES: ixs->headroom += sizeof(struct esphdr); break; #endif /* CONFIG_IPSEC_ENC_3DES */ default: ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_ESP_BADALG; goto cleanup; } #ifdef CONFIG_IPSEC_ALG if ((ixt_a=ixs->ipsp->ips_alg_auth)) { ixs->tailroom += AHHMAC_HASHLEN; } else #endif /* CONFIG_IPSEC_ALG */ switch(ixs->ipsp->ips_authalg) { #ifdef CONFIG_IPSEC_AUTH_HMAC_MD5 case AH_MD5: ixs->tailroom += AHHMAC_HASHLEN; break; #endif /* CONFIG_IPSEC_AUTH_HMAC_MD5 */ #ifdef CONFIG_IPSEC_AUTH_HMAC_SHA1 case AH_SHA: ixs->tailroom += AHHMAC_HASHLEN; break; #endif /* CONFIG_IPSEC_AUTH_HMAC_SHA1 */ case AH_NONE: break; default: ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_AH_BADALG; goto cleanup; } #ifdef CONFIG_IPSEC_ALG ixs->tailroom += blocksize != 1 ? ((blocksize - ((ixs->pyldsz + 2) % blocksize)) % blocksize) + 2 : ((4 - ((ixs->pyldsz + 2) % 4)) % 4) + 2; #else ixs->tailroom += ((8 - ((ixs->pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2; #endif /* CONFIG_IPSEC_ALG */ #ifdef CONFIG_IPSEC_NAT_TRAVERSAL if ((ixs->ipsp->ips_natt_type) && (!ixs->natt_type)) { ixs->natt_type = ixs->ipsp->ips_natt_type; ixs->natt_sport = ixs->ipsp->ips_natt_sport; ixs->natt_dport = ixs->ipsp->ips_natt_dport; switch (ixs->natt_type) { case ESPINUDP_WITH_NON_IKE: ixs->natt_head = sizeof(struct udphdr)+(2*sizeof(__u32)); break; case ESPINUDP_WITH_NON_ESP: ixs->natt_head = sizeof(struct udphdr); break; default: ixs->natt_head = 0; break; } ixs->tailroom += ixs->natt_head; } #endif break; #endif /* !CONFIG_IPSEC_ESP */ #ifdef CONFIG_IPSEC_IPIP case IPPROTO_IPIP: ixs->headroom += sizeof(struct iphdr); break; #endif /* !CONFIG_IPSEC_IPIP */ case IPPROTO_COMP: #ifdef CONFIG_IPSEC_IPCOMP /* We can't predict how much the packet will shrink without doing the actual compression. We could do it here, if we were the first encapsulation in the chain. That might save us a skb_copy_expand, since we might fit into the existing skb then. However, this would be a bit unclean (and this hack has bit us once), so we better not do it. After all, the skb_copy_expand is cheap in comparison to the actual compression. At least we know the packet will not grow. */ break; #endif /* CONFIG_IPSEC_IPCOMP */ default: ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_BADPROTO; goto cleanup; } ixs->ipsp = ixs->ipsp->ips_onext; KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "Required head,tailroom: %d,%d\n", ixs->headroom, ixs->tailroom); ixs->max_headroom += ixs->headroom; ixs->max_tailroom += ixs->tailroom; ixs->pyldsz += (ixs->headroom + ixs->tailroom); } ixs->ipsp = ixs->ipsq; /* restore the head of the ipsec_sa chain */ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "existing head,tailroom: %d,%d before applying xforms with head,tailroom: %d,%d .\n", skb_headroom(ixs->skb), skb_tailroom(ixs->skb), ixs->max_headroom, ixs->max_tailroom); ixs->tot_headroom += ixs->max_headroom; ixs->tot_tailroom += ixs->max_tailroom; ixs->mtudiff = ixs->prv->mtu + ixs->tot_headroom + ixs->tot_tailroom - ixs->physmtu; KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "mtu:%d physmtu:%d tothr:%d tottr:%d mtudiff:%d ippkttotlen:%d\n", ixs->prv->mtu, ixs->physmtu, ixs->tot_headroom, ixs->tot_tailroom, ixs->mtudiff, ntohs(ixs->iph->tot_len)); if(ixs->mtudiff > 0) { int newmtu = ixs->physmtu - (ixs->tot_headroom + ((ixs->tot_tailroom + 2) & ~7) + 5); KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_info:ipsec_xmit_encap_bundle: " "dev %s mtu of %d decreased by %d to %d\n", ixs->dev->name, ixs->prv->mtu, ixs->prv->mtu - newmtu, newmtu); ixs->prv->mtu = newmtu; #ifdef NET_21 #if 0 ixs->skb->dst->pmtu = ixs->prv->mtu; /* RGB */ #endif /* 0 */ #else /* NET_21 */ #if 0 ixs->dev->mtu = ixs->prv->mtu; /* RGB */ #endif /* 0 */ #endif /* NET_21 */ } /* If the sender is doing PMTU discovery, and the packet doesn't fit within ixs->prv->mtu, notify him (unless it was an ICMP packet, or it was not the zero-offset packet) and send it anyways. Note: buggy firewall configuration may prevent the ICMP packet from getting back. */ if(sysctl_ipsec_icmp && ixs->prv->mtu < ntohs(ixs->iph->tot_len) && (ixs->iph->frag_off & __constant_htons(IP_DF)) ) { int notify = ixs->iph->protocol != IPPROTO_ICMP && (ixs->iph->frag_off & __constant_htons(IP_OFFSET)) == 0; #ifdef IPSEC_obey_DF KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "fragmentation needed and DF set; %sdropping packet\n", notify ? "sending ICMP and " : ""); if (notify) ICMP_SEND(ixs->skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, ixs->prv->mtu, ixs->physdev); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_CANNOTFRAG; goto cleanup; #else /* IPSEC_obey_DF */ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "fragmentation needed and DF set; %spassing packet\n", notify ? "sending ICMP and " : ""); if (notify) ICMP_SEND(ixs->skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, ixs->prv->mtu, ixs->physdev); #endif /* IPSEC_obey_DF */ } #ifdef MSS_HACK /* * If this is a transport mode TCP packet with * SYN set, determine an effective MSS based on * AH/ESP overheads determined above. */ if (ixs->iph->protocol == IPPROTO_TCP && ixs->outgoing_said.proto != IPPROTO_IPIP) { struct tcphdr *tcph = ixs->skb->h.th; if (tcph->syn && !tcph->ack) { if(!ipsec_adjust_mss(ixs->skb, tcph, ixs->prv->mtu)) { printk(KERN_WARNING "klips_warning:ipsec_xmit_encap_bundle: " "ipsec_adjust_mss() failed\n"); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_MSSERR; goto cleanup; } } } #endif /* MSS_HACK */ #ifdef CONFIG_IPSEC_NAT_TRAVERSAL if ((ixs->natt_type) && (ixs->outgoing_said.proto != IPPROTO_IPIP)) { /** * NAT-Traversal and Transport Mode: * we need to correct TCP/UDP checksum * * If we've got NAT-OA, we can fix checksum without recalculation. * If we don't we can zero udp checksum. */ __u32 natt_oa = ixs->ipsp->ips_natt_oa ? ((struct sockaddr_in*)(ixs->ipsp->ips_natt_oa))->sin_addr.s_addr : 0; __u16 pkt_len = ixs->skb->tail - (unsigned char *)ixs->iph; __u16 data_len = pkt_len - (ixs->iph->ihl << 2); switch (ixs->iph->protocol) { case IPPROTO_TCP: if (data_len >= sizeof(struct tcphdr)) { struct tcphdr *tcp = (struct tcphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); if (natt_oa) { __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: " "fix TCP checksum using NAT-OA\n"); tcp->check = csum_fold( csum_partial((unsigned char *)buff, sizeof(buff), tcp->check^0xffff)); } else { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: do not recalc TCP checksum\n"); } } else { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: can't fix TCP checksum\n"); } break; case IPPROTO_UDP: if (data_len >= sizeof(struct udphdr)) { struct udphdr *udp = (struct udphdr *)((__u32 *)ixs->iph+ixs->iph->ihl); if (udp->check == 0) { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: UDP checksum already 0\n"); } else if (natt_oa) { __u32 buff[2] = { ~ixs->iph->daddr, natt_oa }; KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: " "fix UDP checksum using NAT-OA\n"); udp->check = csum_fold( csum_partial((unsigned char *)buff, sizeof(buff), udp->check^0xffff)); } else { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: zero UDP checksum\n"); udp->check = 0; } } else { KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: can't fix UDP checksum\n"); } break; default: KLIPS_PRINT(debug_tunnel, "klips_debug:ipsec_tunnel_start_xmit: " "NAT-T & TRANSPORT: non TCP/UDP packet -- do nothing\n"); break; } } #endif /* CONFIG_IPSEC_NAT_TRAVERSAL */ if(!ixs->hard_header_stripped) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: " "allocating %d bytes for hardheader.\n", ixs->hard_header_len); if((ixs->saved_header = kmalloc(ixs->hard_header_len, GFP_ATOMIC)) == NULL) { printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: " "Failed, tried to allocate %d bytes for temp hard_header.\n", ixs->hard_header_len); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_ERRMEMALLOC; goto cleanup; } { int i; for (i = 0; i < ixs->hard_header_len; i++) { ixs->saved_header[i] = ixs->skb->data[i]; } } if(ixs->skb->len < ixs->hard_header_len) { printk(KERN_WARNING "klips_error:ipsec_xmit_encap_bundle: " "tried to skb_pull hhlen=%d, %d available. This should never happen, please report.\n", ixs->hard_header_len, (int)(ixs->skb->len)); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_ESP_PUSHPULLERR; goto cleanup; } skb_pull(ixs->skb, ixs->hard_header_len); ixs->hard_header_stripped = 1; /* ixs->iph = (struct iphdr *) (ixs->skb->data); */ KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "head,tailroom: %d,%d after hard_header stripped.\n", skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); KLIPS_IP_PRINT(debug_tunnel & DB_TN_CROUT, ixs->iph); } else { KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "hard header already stripped.\n"); } ixs->ll_headroom = (ixs->hard_header_len + 15) & ~15; if ((skb_headroom(ixs->skb) >= ixs->max_headroom + 2 * ixs->ll_headroom) && (skb_tailroom(ixs->skb) >= ixs->max_tailroom) #ifndef NET_21 && ixs->skb->free #endif /* !NET_21 */ ) { KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "data fits in existing skb\n"); } else { struct sk_buff* tskb; if(!ixs->oskb) { ixs->oskb = ixs->skb; } tskb = skb_copy_expand(ixs->skb, /* The need for 2 * link layer length here remains unexplained...RGB */ ixs->max_headroom + 2 * ixs->ll_headroom, ixs->max_tailroom, GFP_ATOMIC); #ifdef NET_21 if(tskb && ixs->skb->sk) { skb_set_owner_w(tskb, ixs->skb->sk); } #endif /* NET_21 */ if(ixs->skb != ixs->oskb) { ipsec_kfree_skb(ixs->skb); } ixs->skb = tskb; if (!ixs->skb) { printk(KERN_WARNING "klips_debug:ipsec_xmit_encap_bundle: " "Failed, tried to allocate %d head and %d tailroom\n", ixs->max_headroom, ixs->max_tailroom); ixs->stats->tx_errors++; bundle_stat = IPSEC_XMIT_ERRSKBALLOC; goto cleanup; } KLIPS_PRINT(debug_tunnel & DB_TN_CROUT, "klips_debug:ipsec_xmit_encap_bundle: " "head,tailroom: %d,%d after allocation\n", skb_headroom(ixs->skb), skb_tailroom(ixs->skb)); } /* * Apply grouped transforms to packet */ while (ixs->ipsp) { enum ipsec_xmit_value encap_stat = IPSEC_XMIT_OK; encap_stat = ipsec_xmit_encap_once(ixs); if(encap_stat != IPSEC_XMIT_OK) { KLIPS_PRINT(debug_tunnel & DB_TN_XMIT, "klips_debug:ipsec_xmit_encap_bundle: encap_once failed: %d\n", encap_stat); bundle_stat = IPSEC_XMIT_ENCAPFAIL; goto cleanup; } } /* end encapsulation loop here XXX */ cleanup: spin_unlock(&tdb_lock); return bundle_stat; }