Skip to content

Commit 077fbac

Browse files
lcolittiklassert
authored andcommitted
net: xfrm: support setting an output mark.
On systems that use mark-based routing it may be necessary for routing lookups to use marks in order for packets to be routed correctly. An example of such a system is Android, which uses socket marks to route packets via different networks. Currently, routing lookups in tunnel mode always use a mark of zero, making routing incorrect on such systems. This patch adds a new output_mark element to the xfrm state and a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output mark differs from the existing xfrm mark in two ways: 1. The xfrm mark is used to match xfrm policies and states, while the xfrm output mark is used to set the mark (and influence the routing) of the packets emitted by those states. 2. The existing mark is constrained to be a subset of the bits of the originating socket or transformed packet, but the output mark is arbitrary and depends only on the state. The use of a separate mark provides additional flexibility. For example: - A packet subject to two transforms (e.g., transport mode inside tunnel mode) can have two different output marks applied to it, one for the transport mode SA and one for the tunnel mode SA. - On a system where socket marks determine routing, the packets emitted by an IPsec tunnel can be routed based on a mark that is determined by the tunnel, not by the marks of the unencrypted packets. - Support for setting the output marks can be introduced without breaking any existing setups that employ both mark-based routing and xfrm tunnel mode. Simply changing the code to use the xfrm mark for routing output packets could xfrm mark could change behaviour in a way that breaks these setups. If the output mark is unspecified or set to zero, the mark is not set or changed. Tested: make allyesconfig; make -j64 Tested: https://android-review.googlesource.com/452776 Signed-off-by: Lorenzo Colitti <lorenzo@google.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
1 parent f70f250 commit 077fbac

File tree

8 files changed

+47
-20
lines changed

8 files changed

+47
-20
lines changed

include/net/xfrm.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ struct xfrm_state {
165165
int header_len;
166166
int trailer_len;
167167
u32 extra_flags;
168+
u32 output_mark;
168169
} props;
169170

170171
struct xfrm_lifetime_cfg lft;
@@ -298,10 +299,12 @@ struct xfrm_policy_afinfo {
298299
struct dst_entry *(*dst_lookup)(struct net *net,
299300
int tos, int oif,
300301
const xfrm_address_t *saddr,
301-
const xfrm_address_t *daddr);
302+
const xfrm_address_t *daddr,
303+
u32 mark);
302304
int (*get_saddr)(struct net *net, int oif,
303305
xfrm_address_t *saddr,
304-
xfrm_address_t *daddr);
306+
xfrm_address_t *daddr,
307+
u32 mark);
305308
void (*decode_session)(struct sk_buff *skb,
306309
struct flowi *fl,
307310
int reverse);
@@ -1640,7 +1643,7 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
16401643
struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
16411644
const xfrm_address_t *saddr,
16421645
const xfrm_address_t *daddr,
1643-
int family);
1646+
int family, u32 mark);
16441647

16451648
struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp);
16461649

include/uapi/linux/xfrm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ enum xfrm_attr_type_t {
304304
XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */
305305
XFRMA_PAD,
306306
XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */
307+
XFRMA_OUTPUT_MARK, /* __u32 */
307308
__XFRMA_MAX
308309

309310
#define XFRMA_MAX (__XFRMA_MAX - 1)

net/ipv4/xfrm4_policy.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
2121
int tos, int oif,
2222
const xfrm_address_t *saddr,
23-
const xfrm_address_t *daddr)
23+
const xfrm_address_t *daddr,
24+
u32 mark)
2425
{
2526
struct rtable *rt;
2627

2728
memset(fl4, 0, sizeof(*fl4));
2829
fl4->daddr = daddr->a4;
2930
fl4->flowi4_tos = tos;
3031
fl4->flowi4_oif = l3mdev_master_ifindex_by_index(net, oif);
32+
fl4->flowi4_mark = mark;
3133
if (saddr)
3234
fl4->saddr = saddr->a4;
3335

@@ -42,20 +44,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
4244

4345
static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
4446
const xfrm_address_t *saddr,
45-
const xfrm_address_t *daddr)
47+
const xfrm_address_t *daddr,
48+
u32 mark)
4649
{
4750
struct flowi4 fl4;
4851

49-
return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr);
52+
return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark);
5053
}
5154

5255
static int xfrm4_get_saddr(struct net *net, int oif,
53-
xfrm_address_t *saddr, xfrm_address_t *daddr)
56+
xfrm_address_t *saddr, xfrm_address_t *daddr,
57+
u32 mark)
5458
{
5559
struct dst_entry *dst;
5660
struct flowi4 fl4;
5761

58-
dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr);
62+
dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark);
5963
if (IS_ERR(dst))
6064
return -EHOSTUNREACH;
6165

net/ipv6/xfrm6_policy.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727

2828
static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
2929
const xfrm_address_t *saddr,
30-
const xfrm_address_t *daddr)
30+
const xfrm_address_t *daddr,
31+
u32 mark)
3132
{
3233
struct flowi6 fl6;
3334
struct dst_entry *dst;
@@ -36,6 +37,7 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
3637
memset(&fl6, 0, sizeof(fl6));
3738
fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif);
3839
fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
40+
fl6.flowi6_mark = mark;
3941
memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
4042
if (saddr)
4143
memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
@@ -52,12 +54,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
5254
}
5355

5456
static int xfrm6_get_saddr(struct net *net, int oif,
55-
xfrm_address_t *saddr, xfrm_address_t *daddr)
57+
xfrm_address_t *saddr, xfrm_address_t *daddr,
58+
u32 mark)
5659
{
5760
struct dst_entry *dst;
5861
struct net_device *dev;
5962

60-
dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr);
63+
dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark);
6164
if (IS_ERR(dst))
6265
return -EHOSTUNREACH;
6366

net/xfrm/xfrm_device.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
7979
daddr = &x->props.saddr;
8080
}
8181

82-
dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, x->props.family);
82+
dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr,
83+
x->props.family, x->props.output_mark);
8384
if (IS_ERR(dst))
8485
return 0;
8586

net/xfrm/xfrm_output.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
6666
goto error_nolock;
6767
}
6868

69+
if (x->props.output_mark)
70+
skb->mark = x->props.output_mark;
71+
6972
err = x->outer_mode->output(x, skb);
7073
if (err) {
7174
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);

net/xfrm/xfrm_policy.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa
122122
struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
123123
const xfrm_address_t *saddr,
124124
const xfrm_address_t *daddr,
125-
int family)
125+
int family, u32 mark)
126126
{
127127
const struct xfrm_policy_afinfo *afinfo;
128128
struct dst_entry *dst;
@@ -131,7 +131,7 @@ struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
131131
if (unlikely(afinfo == NULL))
132132
return ERR_PTR(-EAFNOSUPPORT);
133133

134-
dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr);
134+
dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr, mark);
135135

136136
rcu_read_unlock();
137137

@@ -143,7 +143,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
143143
int tos, int oif,
144144
xfrm_address_t *prev_saddr,
145145
xfrm_address_t *prev_daddr,
146-
int family)
146+
int family, u32 mark)
147147
{
148148
struct net *net = xs_net(x);
149149
xfrm_address_t *saddr = &x->props.saddr;
@@ -159,7 +159,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
159159
daddr = x->coaddr;
160160
}
161161

162-
dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family);
162+
dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family, mark);
163163

164164
if (!IS_ERR(dst)) {
165165
if (prev_saddr != saddr)
@@ -1340,14 +1340,14 @@ int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk)
13401340

13411341
static int
13421342
xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local,
1343-
xfrm_address_t *remote, unsigned short family)
1343+
xfrm_address_t *remote, unsigned short family, u32 mark)
13441344
{
13451345
int err;
13461346
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
13471347

13481348
if (unlikely(afinfo == NULL))
13491349
return -EINVAL;
1350-
err = afinfo->get_saddr(net, oif, local, remote);
1350+
err = afinfo->get_saddr(net, oif, local, remote, mark);
13511351
rcu_read_unlock();
13521352
return err;
13531353
}
@@ -1378,7 +1378,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
13781378
if (xfrm_addr_any(local, tmpl->encap_family)) {
13791379
error = xfrm_get_saddr(net, fl->flowi_oif,
13801380
&tmp, remote,
1381-
tmpl->encap_family);
1381+
tmpl->encap_family, 0);
13821382
if (error)
13831383
goto fail;
13841384
local = &tmp;
@@ -1598,7 +1598,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
15981598
if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
15991599
family = xfrm[i]->props.family;
16001600
dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
1601-
&saddr, &daddr, family);
1601+
&saddr, &daddr, family,
1602+
xfrm[i]->props.output_mark);
16021603
err = PTR_ERR(dst);
16031604
if (IS_ERR(dst))
16041605
goto put_states;

net/xfrm/xfrm_user.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
584584

585585
xfrm_mark_get(attrs, &x->mark);
586586

587+
if (attrs[XFRMA_OUTPUT_MARK])
588+
x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]);
589+
587590
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]);
588591
if (err)
589592
goto error;
@@ -899,6 +902,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
899902
goto out;
900903
if (x->security)
901904
ret = copy_sec_ctx(x->security, skb);
905+
if (x->props.output_mark) {
906+
ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark);
907+
if (ret)
908+
goto out;
909+
}
902910
out:
903911
return ret;
904912
}
@@ -2454,6 +2462,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
24542462
[XFRMA_PROTO] = { .type = NLA_U8 },
24552463
[XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
24562464
[XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) },
2465+
[XFRMA_OUTPUT_MARK] = { .len = NLA_U32 },
24572466
};
24582467

24592468
static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
@@ -2673,6 +2682,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
26732682
l += nla_total_size(sizeof(x->props.extra_flags));
26742683
if (x->xso.dev)
26752684
l += nla_total_size(sizeof(x->xso));
2685+
if (x->props.output_mark)
2686+
l += nla_total_size(sizeof(x->props.output_mark));
26762687

26772688
/* Must count x->lastused as it may become non-zero behind our back. */
26782689
l += nla_total_size_64bit(sizeof(u64));

0 commit comments

Comments
 (0)