Skip to content

Commit 7a02070

Browse files
Florian Westphalklassert
authored andcommitted
xfrm: policy: replace session decode with flow dissector
xfrm needs to populate ipv4/v6 flow struct for route lookup. In the past there were several bugs in this code: 1. callers that forget to reload header pointers after xfrm_decode_session() (it may pull headers). 2. bugs in decoding where accesses past skb->data occurred. Meanwhile network core gained a packet dissector as well. This switches xfrm to the flow dissector. Changes since RFC: Drop ipv6 mobiliy header support, AFAIU noone uses this. Drop extraction of flowlabel, replaced code doesn't set it either. Reviewed-by: Simon Horman <[email protected]> Link: https://lore.kernel.org/netdev/[email protected]/ Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 45f87dd commit 7a02070

File tree

1 file changed

+95
-158
lines changed

1 file changed

+95
-158
lines changed

net/xfrm/xfrm_policy.c

Lines changed: 95 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,21 @@ struct xfrm_pol_inexact_candidates {
149149
struct hlist_head *res[XFRM_POL_CAND_MAX];
150150
};
151151

152+
struct xfrm_flow_keys {
153+
struct flow_dissector_key_basic basic;
154+
struct flow_dissector_key_control control;
155+
union {
156+
struct flow_dissector_key_ipv4_addrs ipv4;
157+
struct flow_dissector_key_ipv6_addrs ipv6;
158+
} addrs;
159+
struct flow_dissector_key_ip ip;
160+
struct flow_dissector_key_icmp icmp;
161+
struct flow_dissector_key_ports ports;
162+
struct flow_dissector_key_keyid gre;
163+
};
164+
165+
static struct flow_dissector xfrm_session_dissector __ro_after_init;
166+
152167
static DEFINE_SPINLOCK(xfrm_if_cb_lock);
153168
static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly;
154169

@@ -3367,191 +3382,74 @@ xfrm_policy_ok(const struct xfrm_tmpl *tmpl, const struct sec_path *sp, int star
33673382
}
33683383

33693384
static void
3370-
decode_session4(struct sk_buff *skb, struct flowi *fl, bool reverse)
3385+
decode_session4(const struct xfrm_flow_keys *flkeys, struct flowi *fl, bool reverse)
33713386
{
3372-
const struct iphdr *iph = ip_hdr(skb);
3373-
int ihl = iph->ihl;
3374-
u8 *xprth = skb_network_header(skb) + ihl * 4;
33753387
struct flowi4 *fl4 = &fl->u.ip4;
33763388

33773389
memset(fl4, 0, sizeof(struct flowi4));
33783390

3379-
fl4->flowi4_proto = iph->protocol;
3380-
fl4->daddr = reverse ? iph->saddr : iph->daddr;
3381-
fl4->saddr = reverse ? iph->daddr : iph->saddr;
3382-
fl4->flowi4_tos = iph->tos & ~INET_ECN_MASK;
3383-
3384-
if (!ip_is_fragment(iph)) {
3385-
switch (iph->protocol) {
3386-
case IPPROTO_UDP:
3387-
case IPPROTO_UDPLITE:
3388-
case IPPROTO_TCP:
3389-
case IPPROTO_SCTP:
3390-
case IPPROTO_DCCP:
3391-
if (xprth + 4 < skb->data ||
3392-
pskb_may_pull(skb, xprth + 4 - skb->data)) {
3393-
__be16 *ports;
3394-
3395-
xprth = skb_network_header(skb) + ihl * 4;
3396-
ports = (__be16 *)xprth;
3397-
3398-
fl4->fl4_sport = ports[!!reverse];
3399-
fl4->fl4_dport = ports[!reverse];
3400-
}
3401-
break;
3402-
case IPPROTO_ICMP:
3403-
if (xprth + 2 < skb->data ||
3404-
pskb_may_pull(skb, xprth + 2 - skb->data)) {
3405-
u8 *icmp;
3406-
3407-
xprth = skb_network_header(skb) + ihl * 4;
3408-
icmp = xprth;
3409-
3410-
fl4->fl4_icmp_type = icmp[0];
3411-
fl4->fl4_icmp_code = icmp[1];
3412-
}
3413-
break;
3414-
case IPPROTO_GRE:
3415-
if (xprth + 12 < skb->data ||
3416-
pskb_may_pull(skb, xprth + 12 - skb->data)) {
3417-
__be16 *greflags;
3418-
__be32 *gre_hdr;
3419-
3420-
xprth = skb_network_header(skb) + ihl * 4;
3421-
greflags = (__be16 *)xprth;
3422-
gre_hdr = (__be32 *)xprth;
3423-
3424-
if (greflags[0] & GRE_KEY) {
3425-
if (greflags[0] & GRE_CSUM)
3426-
gre_hdr++;
3427-
fl4->fl4_gre_key = gre_hdr[1];
3428-
}
3429-
}
3430-
break;
3431-
default:
3432-
break;
3433-
}
3391+
if (reverse) {
3392+
fl4->saddr = flkeys->addrs.ipv4.dst;
3393+
fl4->daddr = flkeys->addrs.ipv4.src;
3394+
fl4->fl4_sport = flkeys->ports.dst;
3395+
fl4->fl4_dport = flkeys->ports.src;
3396+
} else {
3397+
fl4->saddr = flkeys->addrs.ipv4.src;
3398+
fl4->daddr = flkeys->addrs.ipv4.dst;
3399+
fl4->fl4_sport = flkeys->ports.src;
3400+
fl4->fl4_dport = flkeys->ports.dst;
34343401
}
3402+
3403+
fl4->flowi4_proto = flkeys->basic.ip_proto;
3404+
fl4->flowi4_tos = flkeys->ip.tos;
3405+
fl4->fl4_icmp_type = flkeys->icmp.type;
3406+
fl4->fl4_icmp_type = flkeys->icmp.code;
3407+
fl4->fl4_gre_key = flkeys->gre.keyid;
34353408
}
34363409

34373410
#if IS_ENABLED(CONFIG_IPV6)
34383411
static void
3439-
decode_session6(struct sk_buff *skb, struct flowi *fl, bool reverse)
3412+
decode_session6(const struct xfrm_flow_keys *flkeys, struct flowi *fl, bool reverse)
34403413
{
34413414
struct flowi6 *fl6 = &fl->u.ip6;
3442-
int onlyproto = 0;
3443-
const struct ipv6hdr *hdr = ipv6_hdr(skb);
3444-
u32 offset = sizeof(*hdr);
3445-
struct ipv6_opt_hdr *exthdr;
3446-
const unsigned char *nh = skb_network_header(skb);
3447-
u16 nhoff = IP6CB(skb)->nhoff;
3448-
u8 nexthdr;
3449-
3450-
if (!nhoff)
3451-
nhoff = offsetof(struct ipv6hdr, nexthdr);
3452-
3453-
nexthdr = nh[nhoff];
34543415

34553416
memset(fl6, 0, sizeof(struct flowi6));
34563417

3457-
fl6->daddr = reverse ? hdr->saddr : hdr->daddr;
3458-
fl6->saddr = reverse ? hdr->daddr : hdr->saddr;
3459-
3460-
while (nh + offset + sizeof(*exthdr) < skb->data ||
3461-
pskb_may_pull(skb, nh + offset + sizeof(*exthdr) - skb->data)) {
3462-
nh = skb_network_header(skb);
3463-
exthdr = (struct ipv6_opt_hdr *)(nh + offset);
3464-
3465-
switch (nexthdr) {
3466-
case NEXTHDR_FRAGMENT:
3467-
onlyproto = 1;
3468-
fallthrough;
3469-
case NEXTHDR_ROUTING:
3470-
case NEXTHDR_HOP:
3471-
case NEXTHDR_DEST:
3472-
offset += ipv6_optlen(exthdr);
3473-
nexthdr = exthdr->nexthdr;
3474-
break;
3475-
case IPPROTO_UDP:
3476-
case IPPROTO_UDPLITE:
3477-
case IPPROTO_TCP:
3478-
case IPPROTO_SCTP:
3479-
case IPPROTO_DCCP:
3480-
if (!onlyproto && (nh + offset + 4 < skb->data ||
3481-
pskb_may_pull(skb, nh + offset + 4 - skb->data))) {
3482-
__be16 *ports;
3483-
3484-
nh = skb_network_header(skb);
3485-
ports = (__be16 *)(nh + offset);
3486-
fl6->fl6_sport = ports[!!reverse];
3487-
fl6->fl6_dport = ports[!reverse];
3488-
}
3489-
fl6->flowi6_proto = nexthdr;
3490-
return;
3491-
case IPPROTO_ICMPV6:
3492-
if (!onlyproto && (nh + offset + 2 < skb->data ||
3493-
pskb_may_pull(skb, nh + offset + 2 - skb->data))) {
3494-
u8 *icmp;
3495-
3496-
nh = skb_network_header(skb);
3497-
icmp = (u8 *)(nh + offset);
3498-
fl6->fl6_icmp_type = icmp[0];
3499-
fl6->fl6_icmp_code = icmp[1];
3500-
}
3501-
fl6->flowi6_proto = nexthdr;
3502-
return;
3503-
case IPPROTO_GRE:
3504-
if (!onlyproto &&
3505-
(nh + offset + 12 < skb->data ||
3506-
pskb_may_pull(skb, nh + offset + 12 - skb->data))) {
3507-
struct gre_base_hdr *gre_hdr;
3508-
__be32 *gre_key;
3509-
3510-
nh = skb_network_header(skb);
3511-
gre_hdr = (struct gre_base_hdr *)(nh + offset);
3512-
gre_key = (__be32 *)(gre_hdr + 1);
3513-
3514-
if (gre_hdr->flags & GRE_KEY) {
3515-
if (gre_hdr->flags & GRE_CSUM)
3516-
gre_key++;
3517-
fl6->fl6_gre_key = *gre_key;
3518-
}
3519-
}
3520-
fl6->flowi6_proto = nexthdr;
3521-
return;
3522-
3523-
#if IS_ENABLED(CONFIG_IPV6_MIP6)
3524-
case IPPROTO_MH:
3525-
offset += ipv6_optlen(exthdr);
3526-
if (!onlyproto && (nh + offset + 3 < skb->data ||
3527-
pskb_may_pull(skb, nh + offset + 3 - skb->data))) {
3528-
struct ip6_mh *mh;
3529-
3530-
nh = skb_network_header(skb);
3531-
mh = (struct ip6_mh *)(nh + offset);
3532-
fl6->fl6_mh_type = mh->ip6mh_type;
3533-
}
3534-
fl6->flowi6_proto = nexthdr;
3535-
return;
3536-
#endif
3537-
default:
3538-
fl6->flowi6_proto = nexthdr;
3539-
return;
3540-
}
3418+
if (reverse) {
3419+
fl6->saddr = flkeys->addrs.ipv6.dst;
3420+
fl6->daddr = flkeys->addrs.ipv6.src;
3421+
fl6->fl6_sport = flkeys->ports.dst;
3422+
fl6->fl6_dport = flkeys->ports.src;
3423+
} else {
3424+
fl6->saddr = flkeys->addrs.ipv6.src;
3425+
fl6->daddr = flkeys->addrs.ipv6.dst;
3426+
fl6->fl6_sport = flkeys->ports.src;
3427+
fl6->fl6_dport = flkeys->ports.dst;
35413428
}
3429+
3430+
fl6->flowi6_proto = flkeys->basic.ip_proto;
3431+
fl6->fl6_icmp_type = flkeys->icmp.type;
3432+
fl6->fl6_icmp_type = flkeys->icmp.code;
3433+
fl6->fl6_gre_key = flkeys->gre.keyid;
35423434
}
35433435
#endif
35443436

35453437
int __xfrm_decode_session(struct net *net, struct sk_buff *skb, struct flowi *fl,
35463438
unsigned int family, int reverse)
35473439
{
3440+
struct xfrm_flow_keys flkeys;
3441+
3442+
memset(&flkeys, 0, sizeof(flkeys));
3443+
__skb_flow_dissect(net, skb, &xfrm_session_dissector, &flkeys,
3444+
NULL, 0, 0, 0, FLOW_DISSECTOR_F_STOP_AT_ENCAP);
3445+
35483446
switch (family) {
35493447
case AF_INET:
3550-
decode_session4(skb, fl, reverse);
3448+
decode_session4(&flkeys, fl, reverse);
35513449
break;
35523450
#if IS_ENABLED(CONFIG_IPV6)
35533451
case AF_INET6:
3554-
decode_session6(skb, fl, reverse);
3452+
decode_session6(&flkeys, fl, reverse);
35553453
break;
35563454
#endif
35573455
default:
@@ -4253,8 +4151,47 @@ static struct pernet_operations __net_initdata xfrm_net_ops = {
42534151
.exit = xfrm_net_exit,
42544152
};
42554153

4154+
static const struct flow_dissector_key xfrm_flow_dissector_keys[] = {
4155+
{
4156+
.key_id = FLOW_DISSECTOR_KEY_CONTROL,
4157+
.offset = offsetof(struct xfrm_flow_keys, control),
4158+
},
4159+
{
4160+
.key_id = FLOW_DISSECTOR_KEY_BASIC,
4161+
.offset = offsetof(struct xfrm_flow_keys, basic),
4162+
},
4163+
{
4164+
.key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
4165+
.offset = offsetof(struct xfrm_flow_keys, addrs.ipv4),
4166+
},
4167+
{
4168+
.key_id = FLOW_DISSECTOR_KEY_IPV6_ADDRS,
4169+
.offset = offsetof(struct xfrm_flow_keys, addrs.ipv6),
4170+
},
4171+
{
4172+
.key_id = FLOW_DISSECTOR_KEY_PORTS,
4173+
.offset = offsetof(struct xfrm_flow_keys, ports),
4174+
},
4175+
{
4176+
.key_id = FLOW_DISSECTOR_KEY_GRE_KEYID,
4177+
.offset = offsetof(struct xfrm_flow_keys, gre),
4178+
},
4179+
{
4180+
.key_id = FLOW_DISSECTOR_KEY_IP,
4181+
.offset = offsetof(struct xfrm_flow_keys, ip),
4182+
},
4183+
{
4184+
.key_id = FLOW_DISSECTOR_KEY_ICMP,
4185+
.offset = offsetof(struct xfrm_flow_keys, icmp),
4186+
},
4187+
};
4188+
42564189
void __init xfrm_init(void)
42574190
{
4191+
skb_flow_dissector_init(&xfrm_session_dissector,
4192+
xfrm_flow_dissector_keys,
4193+
ARRAY_SIZE(xfrm_flow_dissector_keys));
4194+
42584195
register_pernet_subsys(&xfrm_net_ops);
42594196
xfrm_dev_init();
42604197
xfrm_input_init();

0 commit comments

Comments
 (0)