当前位置:   article > 正文

基于内核4.19版本的XFRM框架_linux的xfrm框架

linux的xfrm框架

基于 kernel 4.19

xfrm相关的头文件如下:

./include/net/xfrm.h                  和网络相关的结构定义: xfrm_state, xfrm_policy
./include/net/netns/xfrm.h        和namespace相关定义: netns_xfrm
./include/uapi/linux/xfrm.h

XFRM相关的代码在 net/xfrm/ 目录下

  1. struct net {
  2. ...
  3. #ifdef CONFIG_XFRM
  4. struct netns_xfrm xfrm;
  5. #endif
  6. ...
  7. }
  8. void __init xfrm_init(void)
  9. {
  10. register_pernet_subsys(&xfrm_net_ops);
  11. xfrm_dev_init();
  12. seqcount_init(&xfrm_policy_hash_generation);
  13. xfrm_input_init();
  14. RCU_INIT_POINTER(xfrm_if_cb, NULL);
  15. synchronize_rcu();
  16. }
  17. void __init xfrm4_init(void)
  18. {
  19. xfrm4_state_init();
  20. xfrm4_policy_init();
  21. xfrm4_protocol_init();
  22. register_pernet_subsys(&xfrm4_net_ops);
  23. }
  24. static struct pernet_operations __net_initdata xfrm_net_ops = {
  25. .init = xfrm_net_init,
  26. .exit = xfrm_net_exit,
  27. };
  28. static int __net_init xfrm_net_init(struct net *net)
  29. {
  30. ...
  31. rv = xfrm_statistics_init(net);
  32. if (rv < 0)
  33. goto out_statistics;
  34. rv = xfrm_state_init(net);
  35. if (rv < 0)
  36. goto out_state;
  37. rv = xfrm_policy_init(net);
  38. if (rv < 0)
  39. goto out_policy;
  40. ...
  41. }
  42. int __net_init xfrm_state_init(struct net *net)
  43. {
  44. ...
  45. net->xfrm.state_bydst = xfrm_hash_alloc(sz);
  46. if (!net->xfrm.state_bydst)
  47. goto out_bydst;
  48. net->xfrm.state_bysrc = xfrm_hash_alloc(sz);
  49. if (!net->xfrm.state_bysrc)
  50. goto out_bysrc;
  51. net->xfrm.state_byspi = xfrm_hash_alloc(sz);
  52. if (!net->xfrm.state_byspi)
  53. goto out_byspi;
  54. net->xfrm.state_hmask = ((sz / sizeof(struct hlist_head)) - 1);
  55. ...
  56. }

内核 XFRM框架初始化:

void __init xfrm4_init(void)
{
    xfrm4_state_init(); // 将 xfrm4_state_afinfo 添加到 *xfrm_state_afinfo[NPROTO];
    xfrm4_policy_init(); //将 xfrm4_policy_afinfo添加到 *xfrm_policy_afinfo[AF_INET6 + 1]
    xfrm4_protocol_init(); //xfrm4_input_afinfo static 添加到*xfrm_input_afinfo[AF_INET6 + 1];
    register_pernet_subsys(&xfrm4_net_ops);
}

ip_rcv ->  ip_rcv_finish -> ip_rcv_finish_core(查找路由) -> dst_input

ip_rcv_finish_core ->ip_route_input_noref -> ip_route_input_rcu -> ip_route_input_slow -> fib_lookup -> res->type :

a. RTN_BROADCAST

b. RTN_LOCAL -> rt_dst_alloc

```C

1630         rt->dst.output = ip_output;                                                                                                    
1631         if (flags & RTCF_LOCAL)                                                                                                        
1632             rt->dst.input = ip_local_deliver;   

```

1. input: ip_local_deliver
2. output: ip_output

XFRM 初始化:route.c: ip_rt_init

```C

 3246 #ifdef CONFIG_XFRM                                                                                                                                                  
3247     xfrm_init();
3248     xfrm4_init();
3249 #endif

```

3.1 xfrm4_init();

379 void __init xfrm4_init(void)
380 {
381     xfrm4_state_init();//init xfrm_state_afinfo
382     xfrm4_policy_init();//init xfrm_policy_afinfo
383     xfrm4_protocol_init();//set the xfrm 协议回调函数
384     register_pernet_subsys(&xfrm4_net_ops);
385 }

static const struct xfrm_input_afinfo xfrm4_input_afinfo = {
    .family        =    AF_INET,
    .callback    =    xfrm4_rcv_cb,
};

static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
    .dst_ops =        &xfrm4_dst_ops_template,
    .dst_lookup =        xfrm4_dst_lookup,
    .get_saddr =        xfrm4_get_saddr,
    .decode_session =    _decode_session4,
    .get_tos =        xfrm4_get_tos,
    .init_path =        xfrm4_init_path,
    .fill_dst =        xfrm4_fill_dst,
    .blackhole_route =    ipv4_blackhole_route,
};

 74 static struct xfrm_state_afinfo xfrm4_state_afinfo = {
 75     .family         = AF_INET,
 76     .proto          = IPPROTO_IPIP,
 77     .eth_proto      = htons(ETH_P_IP),
 78     .owner          = THIS_MODULE,
 79     .init_flags     = xfrm4_init_flags,
 80     .init_tempsel       = __xfrm4_init_tempsel,
 81     .init_temprop       = xfrm4_init_temprop,
 82     .output         = xfrm4_output,
 83     .output_finish      = xfrm4_output_finish,
 84     .extract_input      = xfrm4_extract_input,
 85     .extract_output     = xfrm4_extract_output,
 86     .transport_finish   = xfrm4_transport_finish,
 87     .local_error        = xfrm4_local_error,
 88 };

  1. 126 static struct xfrm_mode xfrm4_tunnel_mode = {
  2. 127 .input2 = xfrm4_mode_tunnel_input,
  3. 128 .input = xfrm_prepare_input,
  4. 129 .output2 = xfrm4_mode_tunnel_output,
  5. 130 .output = xfrm4_prepare_output,
  6. 131 .gso_segment = xfrm4_mode_tunnel_gso_segment,
  7. 132 .xmit = xfrm4_mode_tunnel_xmit,
  8. 133 .owner = THIS_MODULE,
  9. 134 .encap = XFRM_MODE_TUNNEL,
  10. 135 .flags = XFRM_MODE_FLAG_TUNNEL,
  11. 136 };

## IPSEC接收处理

  1. static int ip_local_deliver_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
  2. {
  3. ...
  4. rcu_read_lock();
  5. {
  6. int protocol = ip_hdr(skb)->protocol;
  7. const struct net_protocol *ipprot;
  8. int raw;
  9. resubmit:
  10. raw = raw_local_deliver(skb, protocol);
  11. ipprot = rcu_dereference(inet_protos[protocol]);
  12. if (ipprot) {
  13. int ret;
  14. if (!ipprot->no_policy) {
  15. if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
  16. kfree_skb(skb);
  17. goto out;
  18. }
  19. nf_reset(skb);
  20. }
  21. ret = ipprot->handler(skb);
  22. if (ret < 0) {
  23. protocol = -ret;
  24. goto resubmit;
  25. }
  26. __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
  27. } else {
  28. if (!raw) {
  29. if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
  30. __IP_INC_STATS(net, IPSTATS_MIB_INUNKNOWNPROTOS);
  31. icmp_send(skb, ICMP_DEST_UNREACH,
  32. ICMP_PROT_UNREACH, 0);
  33. }
  34. kfree_skb(skb);
  35. } else {
  36. __IP_INC_STATS(net, IPSTATS_MIB_INDELIVERS);
  37. consume_skb(skb);
  38. }
  39. }
  40. }
  41. out:
  42. rcu_read_unlock();
  43. return 0;
  44. }

xfrm4_policy_check -> __xfrm_policy_check2 -> __xfrm_policy_check

检查SP 策略

inet_protos 中挂载了各类协议(udp/tcp/icmp/esp/ah/esp)的操作集,对于IPSEC的AH或者ESP协议来说,ipprot->handler(skb) 处理函数就是 xfrm4_rcv, 如果NAT-T情况下,接收处理函数

  1. static struct xfrm4_protocol esp4_protocol = {
  2. .handler = xfrm4_rcv,
  3. .input_handler = xfrm_input,
  4. .cb_handler = esp4_rcv_cb,
  5. .err_handler = esp4_err,
  6. .priority = 0,
  7. };
  1. static struct net_protocol udp_protocol = {
  2. .early_demux = udp_v4_early_demux,
  3. .early_demux_handler = udp_v4_early_demux,
  4. .handler = udp_rcv,
  5. .err_handler = udp_err,
  6. .no_policy = 1,
  7. .netns_ok = 1,
  8. };

 数据包内部就是被IPSEC封装的报文,处理顺序是:

xfrm4_rcv -> xfrm4_rcv_spi -> xfrm_input

xfrm_input 处理

1. secpath_set(skb); 创建IPSEC的安全路径

2. xfrm_parse_spi(skb, nexthdr, &spi, &seq)解析报文,得到报文的SPI, seq 根据AH/ESP头

  1. struct ip_auth_hdr {
  2. __u8 nexthdr;
  3. __u8 hdrlen; /* This one is measured in 32 bit units! */
  4. __be16 reserved;
  5. __be32 spi;
  6. __be32 seq_no; /* Sequence number */
  7. __u8 auth_data[0]; /* Variable len but >=4. Mind the 64 bit alignment! */
  8. };
  9. struct ip_esp_hdr {
  10. __be32 spi;
  11. __be32 seq_no; /* Sequence number */
  12. __u8 enc_data[0]; /* Variable len but >=8. Mind the 64 bit alignment! */
  13. };

3. xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);

根据 daddr、spi、nexthdr(协议类型:ah/esp)查找 SA, 并对SA做比如状态、封装类型、抗重放和SA超时等检查;

nexthdr = x->type->input(x, skb);

调用根据SA相对应的协议类型的 input 处理函数;

  1. static const struct xfrm_type ah_type =
  2. {
  3. .description = "AH4",
  4. .owner = THIS_MODULE,
  5. .proto = IPPROTO_AH,
  6. .flags = XFRM_TYPE_REPLAY_PROT,
  7. .init_state = ah_init_state,
  8. .destructor = ah_destroy,
  9. .input = ah_input,
  10. .output = ah_output
  11. };
  12. static const struct xfrm_type esp_type =
  13. {
  14. .description = "ESP4",
  15. .owner = THIS_MODULE,
  16. .proto = IPPROTO_ESP,
  17. .flags = XFRM_TYPE_REPLAY_PROT,
  18. .init_state = esp_init_state,
  19. .destructor = esp_destroy,
  20. .get_mtu = esp4_get_mtu,
  21. .input = esp_input,
  22. .output = esp_output,
  23. };
  24. static const struct xfrm_type ipcomp_type = {
  25. .description = "IPCOMP4",
  26. .owner = THIS_MODULE,
  27. .proto = IPPROTO_COMP,
  28. .init_state = ipcomp4_init_state,
  29. .destructor = ipcomp_destroy,
  30. .input = ipcomp_input,
  31. .output = ipcomp_output
  32. };

4. esp_input: 根据协议ah/esp类型,调用响应的输入处理函数

skb_cow_data(skb, 0, &trailer); 得到报文中的ESP尾部指针的地址 trailer;

然后找到加密区域 aead,

    ESP_SKB_CB(skb)->tmp = tmp;
    seqhi = esp_tmp_extra(tmp);
    iv = esp_tmp_iv(aead, tmp, seqhilen);
    req = esp_tmp_req(aead, iv);
    sg = esp_req_sg(aead, req);

    esp_input_set_header(skb, seqhi);

    sg_init_table(sg, nfrags);
    err = skb_to_sgvec(skb, sg, 0, skb->len);
    if (unlikely(err < 0)) {
        kfree(tmp);
        goto out;
    }

    skb->ip_summed = CHECKSUM_NONE;

    if ((x->props.flags & XFRM_STATE_ESN))
        aead_request_set_callback(req, 0, esp_input_done_esn, skb);
    else
        aead_request_set_callback(req, 0, esp_input_done, skb);

    aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
    aead_request_set_ad(req, assoclen);

    err = crypto_aead_decrypt(req); //解密

esp_input_done2(skb, err);

5. esp_input_done2

解密完成后,根据IPSEC的模式:传输模式和隧道模式,对数据报文进行处理

esp_remove_trailer(skb); //溢出报文末尾的ESP尾部;

如果是NAT-T,封装的数UDP协议

  1. if (x->encap) {
  2. struct xfrm_encap_tmpl *encap = x->encap;
  3. struct udphdr *uh = (void *)(skb_network_header(skb) + ihl);
  4. /*
  5. * 1) if the NAT-T peer's IP or port changed then
  6. * advertize the change to the keying daemon.
  7. * This is an inbound SA, so just compare
  8. * SRC ports.
  9. */
  10. if (iph->saddr != x->props.saddr.a4 ||
  11. uh->source != encap->encap_sport) {
  12. xfrm_address_t ipaddr;
  13. ipaddr.a4 = iph->saddr;
  14. km_new_mapping(x, &ipaddr, uh->source);
  15. /* XXX: perhaps add an extra
  16. * policy check here, to see
  17. * if we should allow or
  18. * reject a packet from a
  19. * different source
  20. * address/port.
  21. */
  22. }
  23. /*
  24. * 2) ignore UDP/TCP checksums in case
  25. * of NAT-T in Transport Mode, or
  26. * perform other post-processing fixes
  27. * as per draft-ietf-ipsec-udp-encaps-06,
  28. * section 3.1.2
  29. */
  30. if (x->props.mode == XFRM_MODE_TRANSPORT)
  31. skb->ip_summed = CHECKSUM_UNNECESSARY;
  32. }

  得到SKB的IP头部以及长度等,设置一些SKB的其他参数,然后返回xfrm_input函数继续往下执行;

  1. inner_mode = x->inner_mode;
  2. if (x->sel.family == AF_UNSPEC) {
  3. inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
  4. if (inner_mode == NULL) {
  5. XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
  6. goto drop;
  7. }
  8. }
  9. if (inner_mode->input(x, skb)) {
  10. XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
  11. goto drop;
  12. }
  13. if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
  14. decaps = 1;
  15. break;
  16. }

inner_mode = x->inner_mode;

inner_mode  就是 xfrm4_tunnel_mode / xfrm4_transport_mode, 去掉外层的IP头部,并设置一些SKB的数据,隧道模式下得到内层的IP头部;

inner_mode->input(x, skb) : xfrm_prepare_input 或者 xfrm4_transport_input

xfrm_prepare_input 函数处理:

x->outer_mode->afinfo->extract_input(x, skb); //xfrm4_extract_input

inner_mode->input2(x, skb); // xfrm4_mode_tunnel_input

隧道模式下,重设 SKB包的 protocol、IP头、 网络层头部地址、MAC头等,然后重新把SKB放入到 netif_rx 函数,即SKB重新入协议栈

gro_cells_receive

传输模式下,调用xfrm4_transport_input 后,从NF_INET_PRE_ROUTING点重新人协议栈,虽然前面已经经过PREROUTING 和 INPUT点,但解密数据后新的协议和端口号任然可能做NAT,之后重新进行路由选择,调用路由的input函数 skb_dst(skb)->input(skb)

x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async);

xfrm4_transport_finish

    NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
        dev_net(skb->dev), NULL, skb, skb->dev, NULL,
        xfrm4_rcv_encap_finish);

xfrm4_rcv_encap_finish -> ip_route_input_noref -> xfrm4_rcv_encap_finish2 -> dst_input -> skb_dst(skb)->input(skb);

可能是ip_local_deliver()或ip_forward(),完成后面的协议栈。

## IPSEC 发送

xfrm4_output

    return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                net, sk, skb, NULL, skb_dst(skb)->dev,
                __xfrm4_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));

__xfrm4_output

x->outer_mode->afinfo->output_finish(sk, skb);

xfrm4_output_finish -> xfrm_output -> xfrm_output2  -> xfrm_output_resume -> xfrm_output_one -> x->outer_mode->output(x, skb)

x->outer_mode->output(x, skb)  <-> xfrm4_prepare_output -> x->outer_mode->output2(x, skb);

x->outer_mode->output2(x, skb); <-> xfrm4_mode_tunnel_output

## IPSEC 转发

路由查找

ip_queue_xmit -> __ip_queue_xmit -> ip_route_output_ports -> ip_route_output_flow -> xfrm_lookup_route -> xfrm_lookup -> xfrm_lookup_with_ifid :


/* Finds/creates a bundle for given flow and if_id */

xfrm_lookup_with_ifid -> 查找

1. xfrm_sk_policy_lookup -> xfrm_selector_match -> security_xfrm_policy_lookup

2. xfrm_policy_lookup_bytype

xfrm_resolve_and_create_bundle //创建

xfrm_bundle_lookup //如果已经创建了,查找bundle

数据封装发送

ip_queue_xmit -> __ip_queue_xmit -> ip_local_out -> __ip_local_out -> dst_output -> xfrm4_output -> __xfrm4_output -> xfrm4_output_finish -> xfrm_output -> xfrm_output2 -> xfrm_output_resume ->

本文借鉴了: 走进Linux内核之XFRM框架_北岸冷若冰霜的博客-CSDN博客_xfr

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/814548
推荐阅读
相关标签
  

闽ICP备14008679号