Skip to content

Commit 7ac64f4

Browse files
choppsv1klassert
authored andcommitted
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 <[email protected]> Tested-by: Antony Antony <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent f69eb4f commit 7ac64f4

File tree

7 files changed

+159
-10
lines changed

7 files changed

+159
-10
lines changed

include/net/xfrm.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ struct xfrm_state {
213213
u16 family;
214214
xfrm_address_t saddr;
215215
int header_len;
216+
int enc_hdr_len;
216217
int trailer_len;
217218
u32 extra_flags;
218219
struct xfrm_mark smark;
@@ -303,6 +304,9 @@ struct xfrm_state {
303304
* interpreted by xfrm_type methods. */
304305
void *data;
305306
u8 dir;
307+
308+
const struct xfrm_mode_cbs *mode_cbs;
309+
void *mode_data;
306310
};
307311

308312
static inline struct net *xs_net(struct xfrm_state *x)
@@ -460,6 +464,45 @@ struct xfrm_type_offload {
460464
int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned short family);
461465
void xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family);
462466

467+
/**
468+
* struct xfrm_mode_cbs - XFRM mode callbacks
469+
* @owner: module owner or NULL
470+
* @init_state: Add/init mode specific state in `xfrm_state *x`
471+
* @clone_state: Copy mode specific values from `orig` to new state `x`
472+
* @destroy_state: Cleanup mode specific state from `xfrm_state *x`
473+
* @user_init: Process mode specific netlink attributes from user
474+
* @copy_to_user: Add netlink attributes to `attrs` based on state in `x`
475+
* @sa_len: Return space required to store mode specific netlink attributes
476+
* @get_inner_mtu: Return avail payload space after removing encap overhead
477+
* @input: Process received packet from SA using mode
478+
* @output: Output given packet using mode
479+
* @prepare_output: Add mode specific encapsulation to packet in skb. On return
480+
* `transport_header` should point at ESP header, `network_header` should
481+
* point at outer IP header and `mac_header` should opint at the
482+
* protocol/nexthdr field of the outer IP.
483+
*
484+
* One should examine and understand the specific uses of these callbacks in
485+
* xfrm for further detail on how and when these functions are called. RTSL.
486+
*/
487+
struct xfrm_mode_cbs {
488+
struct module *owner;
489+
int (*init_state)(struct xfrm_state *x);
490+
int (*clone_state)(struct xfrm_state *x, struct xfrm_state *orig);
491+
void (*destroy_state)(struct xfrm_state *x);
492+
int (*user_init)(struct net *net, struct xfrm_state *x,
493+
struct nlattr **attrs,
494+
struct netlink_ext_ack *extack);
495+
int (*copy_to_user)(struct xfrm_state *x, struct sk_buff *skb);
496+
unsigned int (*sa_len)(const struct xfrm_state *x);
497+
u32 (*get_inner_mtu)(struct xfrm_state *x, int outer_mtu);
498+
int (*input)(struct xfrm_state *x, struct sk_buff *skb);
499+
int (*output)(struct net *net, struct sock *sk, struct sk_buff *skb);
500+
int (*prepare_output)(struct xfrm_state *x, struct sk_buff *skb);
501+
};
502+
503+
int xfrm_register_mode_cbs(u8 mode, const struct xfrm_mode_cbs *mode_cbs);
504+
void xfrm_unregister_mode_cbs(u8 mode);
505+
463506
static inline int xfrm_af2proto(unsigned int family)
464507
{
465508
switch(family) {

net/xfrm/xfrm_device.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ static void __xfrm_mode_tunnel_prep(struct xfrm_state *x, struct sk_buff *skb,
4242
skb->transport_header = skb->network_header + hsize;
4343

4444
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);
4647
}
4748

4849
static void __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb,

net/xfrm/xfrm_input.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,20 @@ static int xfrm_inner_mode_input(struct xfrm_state *x,
446446
WARN_ON_ONCE(1);
447447
break;
448448
default:
449+
if (x->mode_cbs && x->mode_cbs->input)
450+
return x->mode_cbs->input(x, skb);
451+
449452
WARN_ON_ONCE(1);
450453
break;
451454
}
452455

453456
return -EOPNOTSUPP;
454457
}
455458

459+
/* NOTE: encap_type - In addition to the normal (non-negative) values for
460+
* encap_type, a negative value of -1 or -2 can be used to resume/restart this
461+
* function after a previous invocation early terminated for async operation.
462+
*/
456463
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
457464
{
458465
const struct xfrm_state_afinfo *afinfo;
@@ -489,6 +496,10 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
489496

490497
family = x->props.family;
491498

499+
/* An encap_type of -2 indicates reconstructed inner packet */
500+
if (encap_type == -2)
501+
goto resume_decapped;
502+
492503
/* An encap_type of -1 indicates async resumption. */
493504
if (encap_type == -1) {
494505
async = 1;
@@ -679,11 +690,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
679690

680691
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
681692

682-
if (xfrm_inner_mode_input(x, skb)) {
693+
err = xfrm_inner_mode_input(x, skb);
694+
if (err == -EINPROGRESS)
695+
return 0;
696+
else if (err) {
683697
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
684698
goto drop;
685699
}
686-
700+
resume_decapped:
687701
if (x->outer_mode.flags & XFRM_MODE_FLAG_TUNNEL) {
688702
decaps = 1;
689703
break;

net/xfrm/xfrm_output.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ static int xfrm_outer_mode_output(struct xfrm_state *x, struct sk_buff *skb)
472472
WARN_ON_ONCE(1);
473473
break;
474474
default:
475+
if (x->mode_cbs && x->mode_cbs->prepare_output)
476+
return x->mode_cbs->prepare_output(x, skb);
475477
WARN_ON_ONCE(1);
476478
break;
477479
}

net/xfrm/xfrm_policy.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,13 +2748,17 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
27482748

27492749
dst1->input = dst_discard;
27502750

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+
}
27582762

27592763
xdst_prev = xdst;
27602764

net/xfrm/xfrm_state.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,60 @@ static const struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family)
515515
return NULL;
516516
}
517517

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+
518572
void xfrm_state_free(struct xfrm_state *x)
519573
{
520574
kmem_cache_free(xfrm_state_cache, x);
@@ -523,6 +577,8 @@ EXPORT_SYMBOL(xfrm_state_free);
523577

524578
static void ___xfrm_state_destroy(struct xfrm_state *x)
525579
{
580+
if (x->mode_cbs && x->mode_cbs->destroy_state)
581+
x->mode_cbs->destroy_state(x);
526582
hrtimer_cancel(&x->mtimer);
527583
del_timer_sync(&x->rtimer);
528584
kfree(x->aead);
@@ -682,6 +738,7 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
682738
x->replay_maxdiff = 0;
683739
x->pcpu_num = UINT_MAX;
684740
spin_lock_init(&x->lock);
741+
x->mode_data = NULL;
685742
}
686743
return x;
687744
}
@@ -1945,6 +2002,12 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
19452002
x->new_mapping_sport = 0;
19462003
x->dir = orig->dir;
19472004

2005+
x->mode_cbs = orig->mode_cbs;
2006+
if (x->mode_cbs && x->mode_cbs->clone_state) {
2007+
if (x->mode_cbs->clone_state(x, orig))
2008+
goto error;
2009+
}
2010+
19482011
return x;
19492012

19502013
error:
@@ -2986,6 +3049,9 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu)
29863049
case XFRM_MODE_TUNNEL:
29873050
break;
29883051
default:
3052+
if (x->mode_cbs && x->mode_cbs->get_inner_mtu)
3053+
return x->mode_cbs->get_inner_mtu(x, mtu);
3054+
29893055
WARN_ON_ONCE(1);
29903056
break;
29913057
}
@@ -3086,6 +3152,12 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload,
30863152
}
30873153
}
30883154

3155+
x->mode_cbs = xfrm_get_mode_cbs(x->props.mode);
3156+
if (x->mode_cbs) {
3157+
if (x->mode_cbs->init_state)
3158+
err = x->mode_cbs->init_state(x);
3159+
module_put(x->mode_cbs->owner);
3160+
}
30893161
error:
30903162
return err;
30913163
}

net/xfrm/xfrm_user.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,12 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
932932
goto error;
933933
}
934934

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+
935941
return x;
936942

937943
error:
@@ -1347,6 +1353,10 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
13471353
if (ret)
13481354
goto out;
13491355
}
1356+
if (x->mode_cbs && x->mode_cbs->copy_to_user)
1357+
ret = x->mode_cbs->copy_to_user(x, skb);
1358+
if (ret)
1359+
goto out;
13501360
if (x->mapping_maxage) {
13511361
ret = nla_put_u32(skb, XFRMA_MTIMER_THRESH, x->mapping_maxage);
13521362
if (ret)
@@ -3606,6 +3616,9 @@ static inline unsigned int xfrm_sa_len(struct xfrm_state *x)
36063616
if (x->nat_keepalive_interval)
36073617
l += nla_total_size(sizeof(x->nat_keepalive_interval));
36083618

3619+
if (x->mode_cbs && x->mode_cbs->sa_len)
3620+
l += x->mode_cbs->sa_len(x);
3621+
36093622
return l;
36103623
}
36113624

0 commit comments

Comments
 (0)