esp.c
来自「xen 3.2.2 源码」· C语言 代码 · 共 904 行 · 第 1/2 页
C
904 行
//todo Maybe not safe to do this anymore. memmove(skb->mac.raw + head_n, skb->mac.raw, (skb->data - skb->mac.raw)); skb->mac.raw += head_n; skb->nh.raw += head_n; // Move skb->data back to ethernet header. // Do in 2 moves to ensure offsets are +ve, // since args to skb_pull/skb_push are unsigned. skb_pull_vn(skb, head_n); __skb_push(skb, skb->data - skb->mac.raw); // After this esph is invalid. esph = NULL; // Trim padding, restore protocol in IP header. pad = skb_trim_tail(skb, ESP_PAD_N); text_n -= ESP_PAD_N; if((pad->pad_n > 255) | (pad->pad_n > text_n)){ wprintf("> Invalid padding: pad_n=%d text_n=%d\n", pad->pad_n, text_n); goto exit; } skb_trim_tail(skb, pad->pad_n); skb->nh.iph->protocol = pad->protocol; err = skb_push_context(skb, vnet, sa->ident.addr, IPPROTO_ESP, sa, esp_context_free_fn); if(err) goto exit; // Increase sa refcount now the skb context refers to it. // Refcount is decreased by esp_context_free_fn. SAState_incref(sa); // Deliver skb to be received by network code. // Not safe to refer to the skb after this. // todo: return -skb->nh.iph->protocol instead? netif_rx(skb); exit: if(mine){ if(err < 0){ kfree_skb(skb); } err = 1; } dprintf("< skb=%p err=%d\n", skb, err); return err;}/** Estimate the packet size for some data using ESP processing. * * @param sa ESP SA * @param data_n data size * @return size after ESP processing */static u32 esp_sa_size(SAState *sa, int data_n){ // Even in transport mode have to round up to blocksize. // Have to add some padding for alignment even if pad_n is zero. ESPState *esp = sa->data; data_n = roundupto(data_n + ESP_PAD_N, esp->cipher.block_n); if(esp->cipher.pad_n > 0){ data_n = roundupto(data_n, esp->cipher.pad_n); } data_n += esp->digest.icv_n; //data_n += esp->cipher.iv_n; data_n += ESP_HDR_N; return data_n;}/** Compute an icv using HMAC digest. * * @param esp ESP state * @param skb packet to digest * @param offset offset to start at * @param len number of bytes to digest * @param icv return parameter for ICV * @return 0 on success, negative error code otherwise */static inline void esp_hmac_digest(ESPState *esp, struct sk_buff *skb, int offset, int len, u8 *icv){ int err = 0; struct crypto_tfm *digest = esp->digest.tfm; char *icv_tmp = esp->digest.icv_tmp; int sg_n = skb_shinfo(skb)->nr_frags + 1; struct scatterlist sg[sg_n]; dprintf("> offset=%d len=%d\n", offset, len); memset(icv, 0, esp->digest.icv_n); if(DEBUG_ICV){ dprintf("> key len=%d\n", esp->digest.key_n); printk("\nkey="); buf_print(esp->digest.key,esp->digest.key_n); } crypto_hmac_init(digest, esp->digest.key, &esp->digest.key_n); err = skb_scatterlist(skb, sg, &sg_n, offset, len); crypto_hmac_update(digest, sg, sg_n); crypto_hmac_final(digest, esp->digest.key, &esp->digest.key_n, icv_tmp); if(DEBUG_ICV){ dprintf("> digest len=%d ", esp->digest.icv_n); printk("\nval="); buf_print(icv_tmp, esp->digest.icv_n); } memcpy(icv, icv_tmp, esp->digest.icv_n); dprintf("<\n");}/** Finish up an esp state. * Releases the digest, cipher, iv and frees the state. * * @parma esp state */static void esp_fini(ESPState *esp){ if(!esp) return; if(esp->digest.tfm){ crypto_free_tfm(esp->digest.tfm); esp->digest.tfm = NULL; } if(esp->digest.icv_tmp){ kfree(esp->digest.icv_tmp); esp->digest.icv_tmp = NULL; } if(esp->cipher.tfm){ crypto_free_tfm(esp->cipher.tfm); esp->cipher.tfm = NULL; } if(esp->cipher.iv){ kfree(esp->cipher.iv); esp->cipher.iv = NULL; } kfree(esp);}/** Release an ESP SA. * * @param sa ESO SA */static void esp_sa_fini(SAState *sa){ ESPState *esp; if(!sa) return; esp = sa->data; if(!esp) return; esp_fini(esp); sa->data = NULL;}/** Initialize the cipher for an ESP SA. * * @param sa ESP SA * @param esp ESP state * @return 0 on success, negative error code otherwise */static int esp_cipher_init(SAState *sa, ESPState *esp){ int err = 0; SAAlgorithm *algo = NULL; int cipher_mode = CRYPTO_TFM_MODE_CBC; dprintf("> sa=%p esp=%p\n", sa, esp); dprintf("> cipher=%s\n", sa->cipher.name); algo = sa_cipher_by_name(sa->cipher.name); if(!algo){ wprintf("> Cipher unavailable: %s\n", sa->cipher.name); err = -EINVAL; goto exit; } esp->cipher.key_n = roundupto(sa->cipher.bits, 8); // If cipher is null must use ECB because CBC algo does not support blocksize 1. if(strcmp(sa->cipher.name, "cipher_null")){ cipher_mode = CRYPTO_TFM_MODE_ECB; } esp->cipher.tfm = crypto_alloc_tfm(sa->cipher.name, cipher_mode); if(!esp->cipher.tfm){ err = -ENOMEM; goto exit; } esp->cipher.block_n = roundupto(crypto_tfm_alg_blocksize(esp->cipher.tfm), 4); esp->cipher.iv_n = crypto_tfm_alg_ivsize(esp->cipher.tfm); esp->cipher.pad_n = 0; if(esp->cipher.iv_n){ esp->cipher.iv = kmalloc(esp->cipher.iv_n, GFP_KERNEL); get_random_bytes(esp->cipher.iv, esp->cipher.iv_n); } crypto_cipher_setkey(esp->cipher.tfm, esp->cipher.key, esp->cipher.key_n); err = 0; exit: dprintf("< err=%d\n", err); return err;}/** Initialize the digest for an ESP SA. * * @param sa ESP SA * @param esp ESP state * @return 0 on success, negative error code otherwise */static int esp_digest_init(SAState *sa, ESPState *esp){ int err = 0; SAAlgorithm *algo = NULL; dprintf(">\n"); esp->digest.key = sa->digest.key; esp->digest.key_n = bits_to_bytes(roundupto(sa->digest.bits, 8)); esp->digest.tfm = crypto_alloc_tfm(sa->digest.name, 0); if(!esp->digest.tfm){ err = -ENOMEM; goto exit; } algo = sa_digest_by_name(sa->digest.name); if(!algo){ wprintf("> Digest unavailable: %s\n", sa->digest.name); err = -EINVAL; goto exit; } esp->digest.icv = esp_hmac_digest; esp->digest.icv_full_n = bits_to_bytes(algo->info.digest.icv_fullbits); esp->digest.icv_n = bits_to_bytes(algo->info.digest.icv_truncbits); if(esp->digest.icv_full_n != crypto_tfm_alg_digestsize(esp->digest.tfm)){ err = -EINVAL; wprintf("> digest %s, size %u != %hu\n", sa->digest.name, crypto_tfm_alg_digestsize(esp->digest.tfm), esp->digest.icv_full_n); goto exit; } esp->digest.icv_tmp = kmalloc(esp->digest.icv_full_n, GFP_KERNEL); if(!esp->digest.icv_tmp){ err = -ENOMEM; goto exit; } exit: dprintf("< err=%d\n", err); return err;}/** Initialize an ESP SA. * * @param sa ESP SA * @param args arguments * @return 0 on success, negative error code otherwise */static int esp_sa_init(SAState *sa, void *args){ int err = 0; ESPState *esp = NULL; dprintf("> sa=%p\n", sa); esp = kmalloc(sizeof(*esp), GFP_KERNEL); if(!esp){ err = -ENOMEM; goto exit; } *esp = (ESPState){}; err = esp_cipher_init(sa, esp); if(err) goto exit; err = esp_digest_init(sa, esp); if(err) goto exit; sa->data = esp; exit: if(err){ if(esp) esp_fini(esp); } dprintf("< err=%d\n", err); return err;}/** SA type for ESP. */static SAType esp_sa_type = { .name = "ESP", .protocol = IPPROTO_ESP, .init = esp_sa_init, .fini = esp_sa_fini, .size = esp_sa_size, .recv = esp_sa_recv, .send = esp_sa_send};/** Get the ESP header from a packet. * * @param skb packet * @param esph return parameter for header * @return 0 on success, negative error code otherwise */static int esp_skb_header(struct sk_buff *skb, ESPHdr **esph){ int err = 0; if(skb->len < ESP_HDR_N){ err = -EINVAL; goto exit; } *esph = (ESPHdr*)skb->data; exit: return err;}/** Handle an incoming skb with ESP protocol. * * Lookup spi, if state found hand to the state. * If no state, check spi, if ok, create state and pass to it. * If spi not ok, drop. * * Return value convention for protocols: * >= 0 Protocol took the packet * < 0 A -ve protocol id the packet should be re-received as. * * So always return >=0 if we took the packet, even if we dropped it. * * @param skb packet * @return 0 on sucess, negative protocol number otherwise */static int esp_protocol_recv(struct sk_buff *skb){ int err = 0; const int eth_n = ETH_HLEN; int ip_n; ESPHdr *esph = NULL; SAState *sa = NULL; u32 addr; dprintf(">\n");#ifdef DEBUG dprintf("> recv skb=\n"); skb_print_bits("", skb, 0, skb->len);#endif ip_n = (skb->nh.iph->ihl << 2); if(skb->data == skb->mac.raw){ // skb->data points at ethernet header. if (!pskb_may_pull(skb, eth_n + ip_n)){ wprintf("> Malformed skb\n"); err = -EINVAL; goto exit; } skb_pull_vn(skb, eth_n + ip_n); } addr = skb->nh.iph->daddr; err = esp_skb_header(skb, &esph); if(err) goto exit; dprintf("> spi=%08x protocol=%d addr=" IPFMT "\n", esph->spi, IPPROTO_ESP, NIPQUAD(addr)); sa = sa_table_lookup_spi(esph->spi, IPPROTO_ESP, addr); if(!sa){ err = vnet_sa_create(esph->spi, IPPROTO_ESP, addr, &sa); if(err) goto exit; } //todo: Return a -ve protocol instead? See esp_sa_recv. err = SAState_recv(sa, skb); exit: if(sa) SAState_decref(sa); if(err <= 0){ kfree_skb(skb); err = 0; } dprintf("< err=%d\n", err); return err;}/** Handle an ICMP error related to ESP. * * @param skb ICMP error packet * @param info */static void esp_protocol_icmp_err(struct sk_buff *skb, u32 info){ struct iphdr *iph = (struct iphdr*)skb->data; ESPHdr *esph; SAState *sa; dprintf("> ICMP error type=%d code=%d\n", skb->h.icmph->type, skb->h.icmph->code); if(skb->h.icmph->type != ICMP_DEST_UNREACH || skb->h.icmph->code != ICMP_FRAG_NEEDED){ return; } //todo: need to check skb has enough len to do this. esph = (ESPHdr*)(skb->data + (iph->ihl << 2)); sa = sa_table_lookup_spi(esph->spi, IPPROTO_ESP, iph->daddr); if(!sa) return; wprintf("> ICMP unreachable on SA ESP spi=%08x addr=" IPFMT "\n", ntohl(esph->spi), NIPQUAD(iph->daddr)); SAState_decref(sa);}//============================================================================#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)// Code for 2.6 kernel./** Protocol handler for ESP. */static struct net_protocol esp_protocol = { .handler = esp_protocol_recv, .err_handler = esp_protocol_icmp_err};static int esp_protocol_add(void){ return inet_add_protocol(&esp_protocol, IPPROTO_ESP);}static int esp_protocol_del(void){ return inet_del_protocol(&esp_protocol, IPPROTO_ESP);}//============================================================================#else//============================================================================// Code for 2.4 kernel./** Protocol handler for ESP. */static struct inet_protocol esp_protocol = { .name = "ESP", .protocol = IPPROTO_ESP, .handler = esp_protocol_recv, .err_handler = esp_protocol_icmp_err};static int esp_protocol_add(void){ inet_add_protocol(&esp_protocol); return 0;}static int esp_protocol_del(void){ return inet_del_protocol(&esp_protocol);}#endif//============================================================================/** Initialize the ESP module. * Registers the ESP protocol and SA type. * * @return 0 on success, negative error code otherwise */int __init esp_module_init(void){ int err = 0; dprintf(">\n"); err = SAType_add(&esp_sa_type); if(err < 0){ eprintf("> Error adding esp sa type\n"); goto exit; } esp_protocol_add(); exit: dprintf("< err=%d\n", err); return err;}/** Finalize the ESP module. * Deregisters the ESP protocol and SA type. */void __exit esp_module_exit(void){ if(esp_protocol_del() < 0){ eprintf("> Error removing esp protocol\n"); } if(SAType_del(&esp_sa_type) < 0){ eprintf("> Error removing esp sa type\n"); }}#endif // CONFIG_CRYPTO_HMAC
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?