Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfrm: add mode_cbs module functionality

Add a set of callbacks xfrm_mode_cbs to xfrm_state. These callbacks
enable the addition of new xfrm modes, such as IP-TFS to be defined
in modules.

Signed-off-by: Christian Hopps <chopps@labn.net>
Tested-by: Antony Antony <antony.antony@secunet.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Christian Hopps and committed by
Steffen Klassert
7ac64f45 f69eb4f6

+159 -10
+43
include/net/xfrm.h
··· 213 213 u16 family; 214 214 xfrm_address_t saddr; 215 215 int header_len; 216 + int enc_hdr_len; 216 217 int trailer_len; 217 218 u32 extra_flags; 218 219 struct xfrm_mark smark; ··· 304 303 * interpreted by xfrm_type methods. */ 305 304 void *data; 306 305 u8 dir; 306 + 307 + const struct xfrm_mode_cbs *mode_cbs; 308 + void *mode_data; 307 309 }; 308 310 309 311 static inline struct net *xs_net(struct xfrm_state *x) ··· 463 459 464 460 int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned short family); 465 461 void xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); 462 + 463 + /** 464 + * struct xfrm_mode_cbs - XFRM mode callbacks 465 + * @owner: module owner or NULL 466 + * @init_state: Add/init mode specific state in `xfrm_state *x` 467 + * @clone_state: Copy mode specific values from `orig` to new state `x` 468 + * @destroy_state: Cleanup mode specific state from `xfrm_state *x` 469 + * @user_init: Process mode specific netlink attributes from user 470 + * @copy_to_user: Add netlink attributes to `attrs` based on state in `x` 471 + * @sa_len: Return space required to store mode specific netlink attributes 472 + * @get_inner_mtu: Return avail payload space after removing encap overhead 473 + * @input: Process received packet from SA using mode 474 + * @output: Output given packet using mode 475 + * @prepare_output: Add mode specific encapsulation to packet in skb. On return 476 + * `transport_header` should point at ESP header, `network_header` should 477 + * point at outer IP header and `mac_header` should opint at the 478 + * protocol/nexthdr field of the outer IP. 479 + * 480 + * One should examine and understand the specific uses of these callbacks in 481 + * xfrm for further detail on how and when these functions are called. RTSL. 482 + */ 483 + struct xfrm_mode_cbs { 484 + struct module *owner; 485 + int (*init_state)(struct xfrm_state *x); 486 + int (*clone_state)(struct xfrm_state *x, struct xfrm_state *orig); 487 + void (*destroy_state)(struct xfrm_state *x); 488 + int (*user_init)(struct net *net, struct xfrm_state *x, 489 + struct nlattr **attrs, 490 + struct netlink_ext_ack *extack); 491 + int (*copy_to_user)(struct xfrm_state *x, struct sk_buff *skb); 492 + unsigned int (*sa_len)(const struct xfrm_state *x); 493 + u32 (*get_inner_mtu)(struct xfrm_state *x, int outer_mtu); 494 + int (*input)(struct xfrm_state *x, struct sk_buff *skb); 495 + int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb); 496 + int (*prepare_output)(struct xfrm_state *x, struct sk_buff *skb); 497 + }; 498 + 499 + int xfrm_register_mode_cbs(u8 mode, const struct xfrm_mode_cbs *mode_cbs); 500 + void xfrm_unregister_mode_cbs(u8 mode); 466 501 467 502 static inline int xfrm_af2proto(unsigned int family) 468 503 {
+2 -1
net/xfrm/xfrm_device.c
··· 42 42 skb->transport_header = skb->network_header + hsize; 43 43 44 44 skb_reset_mac_len(skb); 45 - pskb_pull(skb, skb->mac_len + x->props.header_len); 45 + pskb_pull(skb, 46 + skb->mac_len + x->props.header_len - x->props.enc_hdr_len); 46 47 } 47 48 48 49 static void __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb,
+16 -2
net/xfrm/xfrm_input.c
··· 446 446 WARN_ON_ONCE(1); 447 447 break; 448 448 default: 449 + if (x->mode_cbs && x->mode_cbs->input) 450 + return x->mode_cbs->input(x, skb); 451 + 449 452 WARN_ON_ONCE(1); 450 453 break; 451 454 } ··· 456 453 return -EOPNOTSUPP; 457 454 } 458 455 456 + /* NOTE: encap_type - In addition to the normal (non-negative) values for 457 + * encap_type, a negative value of -1 or -2 can be used to resume/restart this 458 + * function after a previous invocation early terminated for async operation. 459 + */ 459 460 int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) 460 461 { 461 462 const struct xfrm_state_afinfo *afinfo; ··· 495 488 } 496 489 497 490 family = x->props.family; 491 + 492 + /* An encap_type of -2 indicates reconstructed inner packet */ 493 + if (encap_type == -2) 494 + goto resume_decapped; 498 495 499 496 /* An encap_type of -1 indicates async resumption. */ 500 497 if (encap_type == -1) { ··· 690 679 691 680 XFRM_MODE_SKB_CB(skb)->protocol = nexthdr; 692 681 693 - if (xfrm_inner_mode_input(x, skb)) { 682 + err = xfrm_inner_mode_input(x, skb); 683 + if (err == -EINPROGRESS) 684 + return 0; 685 + else if (err) { 694 686 XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR); 695 687 goto drop; 696 688 } 697 - 689 + resume_decapped: 698 690 if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL) { 699 691 decaps = 1; 700 692 break;
+2
net/xfrm/xfrm_output.c
··· 472 472 WARN_ON_ONCE(1); 473 473 break; 474 474 default: 475 + if (x->mode_cbs && x->mode_cbs->prepare_output) 476 + return x->mode_cbs->prepare_output(x, skb); 475 477 WARN_ON_ONCE(1); 476 478 break; 477 479 }
+11 -7
net/xfrm/xfrm_policy.c
··· 2748 2748 2749 2749 dst1->input = dst_discard; 2750 2750 2751 - rcu_read_lock(); 2752 - afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); 2753 - if (likely(afinfo)) 2754 - dst1->output = afinfo->output; 2755 - else 2756 - dst1->output = dst_discard_out; 2757 - rcu_read_unlock(); 2751 + if (xfrm[i]->mode_cbs && xfrm[i]->mode_cbs->output) { 2752 + dst1->output = xfrm[i]->mode_cbs->output; 2753 + } else { 2754 + rcu_read_lock(); 2755 + afinfo = xfrm_state_afinfo_get_rcu(inner_mode->family); 2756 + if (likely(afinfo)) 2757 + dst1->output = afinfo->output; 2758 + else 2759 + dst1->output = dst_discard_out; 2760 + rcu_read_unlock(); 2761 + } 2758 2762 2759 2763 xdst_prev = xdst; 2760 2764
+72
net/xfrm/xfrm_state.c
··· 515 515 return NULL; 516 516 } 517 517 518 + static const struct xfrm_mode_cbs __rcu *xfrm_mode_cbs_map[XFRM_MODE_MAX]; 519 + static DEFINE_SPINLOCK(xfrm_mode_cbs_map_lock); 520 + 521 + int xfrm_register_mode_cbs(u8 mode, const struct xfrm_mode_cbs *mode_cbs) 522 + { 523 + if (mode >= XFRM_MODE_MAX) 524 + return -EINVAL; 525 + 526 + spin_lock_bh(&xfrm_mode_cbs_map_lock); 527 + rcu_assign_pointer(xfrm_mode_cbs_map[mode], mode_cbs); 528 + spin_unlock_bh(&xfrm_mode_cbs_map_lock); 529 + 530 + return 0; 531 + } 532 + EXPORT_SYMBOL(xfrm_register_mode_cbs); 533 + 534 + void xfrm_unregister_mode_cbs(u8 mode) 535 + { 536 + if (mode >= XFRM_MODE_MAX) 537 + return; 538 + 539 + spin_lock_bh(&xfrm_mode_cbs_map_lock); 540 + RCU_INIT_POINTER(xfrm_mode_cbs_map[mode], NULL); 541 + spin_unlock_bh(&xfrm_mode_cbs_map_lock); 542 + synchronize_rcu(); 543 + } 544 + EXPORT_SYMBOL(xfrm_unregister_mode_cbs); 545 + 546 + static const struct xfrm_mode_cbs *xfrm_get_mode_cbs(u8 mode) 547 + { 548 + const struct xfrm_mode_cbs *cbs; 549 + bool try_load = true; 550 + 551 + if (mode >= XFRM_MODE_MAX) 552 + return NULL; 553 + 554 + retry: 555 + rcu_read_lock(); 556 + 557 + cbs = rcu_dereference(xfrm_mode_cbs_map[mode]); 558 + if (cbs && !try_module_get(cbs->owner)) 559 + cbs = NULL; 560 + 561 + rcu_read_unlock(); 562 + 563 + if (mode == XFRM_MODE_IPTFS && !cbs && try_load) { 564 + request_module("xfrm-iptfs"); 565 + try_load = false; 566 + goto retry; 567 + } 568 + 569 + return cbs; 570 + } 571 + 518 572 void xfrm_state_free(struct xfrm_state *x) 519 573 { 520 574 kmem_cache_free(xfrm_state_cache, x); ··· 577 523 578 524 static void ___xfrm_state_destroy(struct xfrm_state *x) 579 525 { 526 + if (x->mode_cbs && x->mode_cbs->destroy_state) 527 + x->mode_cbs->destroy_state(x); 580 528 hrtimer_cancel(&x->mtimer); 581 529 del_timer_sync(&x->rtimer); 582 530 kfree(x->aead); ··· 738 682 x->replay_maxdiff = 0; 739 683 x->pcpu_num = UINT_MAX; 740 684 spin_lock_init(&x->lock); 685 + x->mode_data = NULL; 741 686 } 742 687 return x; 743 688 } ··· 2002 1945 x->new_mapping_sport = 0; 2003 1946 x->dir = orig->dir; 2004 1947 1948 + x->mode_cbs = orig->mode_cbs; 1949 + if (x->mode_cbs && x->mode_cbs->clone_state) { 1950 + if (x->mode_cbs->clone_state(x, orig)) 1951 + goto error; 1952 + } 1953 + 2005 1954 return x; 2006 1955 2007 1956 error: ··· 3049 2986 case XFRM_MODE_TUNNEL: 3050 2987 break; 3051 2988 default: 2989 + if (x->mode_cbs && x->mode_cbs->get_inner_mtu) 2990 + return x->mode_cbs->get_inner_mtu(x, mtu); 2991 + 3052 2992 WARN_ON_ONCE(1); 3053 2993 break; 3054 2994 } ··· 3152 3086 } 3153 3087 } 3154 3088 3089 + x->mode_cbs = xfrm_get_mode_cbs(x->props.mode); 3090 + if (x->mode_cbs) { 3091 + if (x->mode_cbs->init_state) 3092 + err = x->mode_cbs->init_state(x); 3093 + module_put(x->mode_cbs->owner); 3094 + } 3155 3095 error: 3156 3096 return err; 3157 3097 }
+13
net/xfrm/xfrm_user.c
··· 932 932 goto error; 933 933 } 934 934 935 + if (x->mode_cbs && x->mode_cbs->user_init) { 936 + err = x->mode_cbs->user_init(net, x, attrs, extack); 937 + if (err) 938 + goto error; 939 + } 940 + 935 941 return x; 936 942 937 943 error: ··· 1353 1347 if (ret) 1354 1348 goto out; 1355 1349 } 1350 + if (x->mode_cbs && x->mode_cbs->copy_to_user) 1351 + ret = x->mode_cbs->copy_to_user(x, skb); 1352 + if (ret) 1353 + goto out; 1356 1354 if (x->mapping_maxage) { 1357 1355 ret = nla_put_u32(skb, XFRMA_MTIMER_THRESH, x->mapping_maxage); 1358 1356 if (ret) ··· 3615 3605 3616 3606 if (x->nat_keepalive_interval) 3617 3607 l += nla_total_size(sizeof(x->nat_keepalive_interval)); 3608 + 3609 + if (x->mode_cbs && x->mode_cbs->sa_len) 3610 + l += x->mode_cbs->sa_len(x); 3618 3611 3619 3612 return l; 3620 3613 }