can: cangw: introduce optional uid to reference created routing jobs

Similar to referencing iptables rules by their line number this UID allows to
reference created routing jobs, e.g. to alter configured data modifications.

The UID is an optional non-zero value which can be provided at routing job
creation time. When the UID is set the UID replaces the data modification
configuration as job identification attribute e.g. at job removal time.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Oliver Hartkopp 2015-06-09 08:05:10 +02:00 committed by Marc Kleine-Budde
parent 3d5db5e131
commit dd895d7f21
2 changed files with 61 additions and 12 deletions

View File

@ -78,6 +78,7 @@ enum {
CGW_FILTER, /* specify struct can_filter on source CAN device */ CGW_FILTER, /* specify struct can_filter on source CAN device */
CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */ CGW_DELETED, /* number of deleted CAN frames (see max_hops param) */
CGW_LIM_HOPS, /* limit the number of hops of this specific rule */ CGW_LIM_HOPS, /* limit the number of hops of this specific rule */
CGW_MOD_UID, /* user defined identifier for modification updates */
__CGW_MAX __CGW_MAX
}; };
@ -162,6 +163,10 @@ enum {
* load time of the can-gw module). This value is used to reduce the number of * load time of the can-gw module). This value is used to reduce the number of
* possible hops for this gateway rule to a value smaller then max_hops. * possible hops for this gateway rule to a value smaller then max_hops.
* *
* CGW_MOD_UID (length 4 bytes):
* Optional non-zero user defined routing job identifier to alter existing
* modification settings at runtime.
*
* CGW_CS_XOR (length 4 bytes): * CGW_CS_XOR (length 4 bytes):
* Set a simple XOR checksum starting with an initial value into * Set a simple XOR checksum starting with an initial value into
* data[result-idx] using data[start-idx] .. data[end-idx] * data[result-idx] using data[start-idx] .. data[end-idx]

View File

@ -110,6 +110,7 @@ struct cf_mod {
void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor); void (*xor)(struct can_frame *cf, struct cgw_csum_xor *xor);
void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8); void (*crc8)(struct can_frame *cf, struct cgw_csum_crc8 *crc8);
} csumfunc; } csumfunc;
u32 uid;
}; };
@ -548,6 +549,11 @@ static int cgw_put_job(struct sk_buff *skb, struct cgw_job *gwj, int type,
goto cancel; goto cancel;
} }
if (gwj->mod.uid) {
if (nla_put_u32(skb, CGW_MOD_UID, gwj->mod.uid) < 0)
goto cancel;
}
if (gwj->mod.csumfunc.crc8) { if (gwj->mod.csumfunc.crc8) {
if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN, if (nla_put(skb, CGW_CS_CRC8, CGW_CS_CRC8_LEN,
&gwj->mod.csum.crc8) < 0) &gwj->mod.csum.crc8) < 0)
@ -619,6 +625,7 @@ static const struct nla_policy cgw_policy[CGW_MAX+1] = {
[CGW_DST_IF] = { .type = NLA_U32 }, [CGW_DST_IF] = { .type = NLA_U32 },
[CGW_FILTER] = { .len = sizeof(struct can_filter) }, [CGW_FILTER] = { .len = sizeof(struct can_filter) },
[CGW_LIM_HOPS] = { .type = NLA_U8 }, [CGW_LIM_HOPS] = { .type = NLA_U8 },
[CGW_MOD_UID] = { .type = NLA_U32 },
}; };
/* check for common and gwtype specific attributes */ /* check for common and gwtype specific attributes */
@ -761,6 +768,10 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod,
else else
mod->csumfunc.xor = cgw_csum_xor_neg; mod->csumfunc.xor = cgw_csum_xor_neg;
} }
if (tb[CGW_MOD_UID]) {
nla_memcpy(&mod->uid, tb[CGW_MOD_UID], sizeof(u32));
}
} }
if (gwtype == CGW_TYPE_CAN_CAN) { if (gwtype == CGW_TYPE_CAN_CAN) {
@ -802,6 +813,8 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
struct rtcanmsg *r; struct rtcanmsg *r;
struct cgw_job *gwj; struct cgw_job *gwj;
struct cf_mod mod;
struct can_can_gw ccgw;
u8 limhops = 0; u8 limhops = 0;
int err = 0; int err = 0;
@ -819,6 +832,36 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
if (r->gwtype != CGW_TYPE_CAN_CAN) if (r->gwtype != CGW_TYPE_CAN_CAN)
return -EINVAL; return -EINVAL;
err = cgw_parse_attr(nlh, &mod, CGW_TYPE_CAN_CAN, &ccgw, &limhops);
if (err < 0)
return err;
if (mod.uid) {
ASSERT_RTNL();
/* check for updating an existing job with identical uid */
hlist_for_each_entry(gwj, &cgw_list, list) {
if (gwj->mod.uid != mod.uid)
continue;
/* interfaces & filters must be identical */
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))
return -EINVAL;
/* update modifications with disabled softirq & quit */
local_bh_disable();
memcpy(&gwj->mod, &mod, sizeof(mod));
local_bh_enable();
return 0;
}
}
/* ifindex == 0 is not allowed for job creation */
if (!ccgw.src_idx || !ccgw.dst_idx)
return -ENODEV;
gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL); gwj = kmem_cache_alloc(cgw_cache, GFP_KERNEL);
if (!gwj) if (!gwj)
return -ENOMEM; return -ENOMEM;
@ -828,18 +871,14 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
gwj->deleted_frames = 0; gwj->deleted_frames = 0;
gwj->flags = r->flags; gwj->flags = r->flags;
gwj->gwtype = r->gwtype; gwj->gwtype = r->gwtype;
gwj->limit_hops = limhops;
err = cgw_parse_attr(nlh, &gwj->mod, CGW_TYPE_CAN_CAN, &gwj->ccgw, /* insert already parsed information */
&limhops); memcpy(&gwj->mod, &mod, sizeof(mod));
if (err < 0) memcpy(&gwj->ccgw, &ccgw, sizeof(ccgw));
goto out;
err = -ENODEV; err = -ENODEV;
/* ifindex == 0 is not allowed for job creation */
if (!gwj->ccgw.src_idx || !gwj->ccgw.dst_idx)
goto out;
gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx); gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx);
if (!gwj->src.dev) if (!gwj->src.dev)
@ -856,8 +895,6 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
if (gwj->dst.dev->type != ARPHRD_CAN) if (gwj->dst.dev->type != ARPHRD_CAN)
goto out; goto out;
gwj->limit_hops = limhops;
ASSERT_RTNL(); ASSERT_RTNL();
err = cgw_register_filter(gwj); err = cgw_register_filter(gwj);
@ -931,8 +968,15 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
if (gwj->limit_hops != limhops) if (gwj->limit_hops != limhops)
continue; continue;
if (memcmp(&gwj->mod, &mod, sizeof(mod))) /* we have a match when uid is enabled and identical */
continue; if (gwj->mod.uid || mod.uid) {
if (gwj->mod.uid != mod.uid)
continue;
} else {
/* no uid => check for identical modifications */
if (memcmp(&gwj->mod, &mod, sizeof(mod)))
continue;
}
/* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */ /* if (r->gwtype == CGW_TYPE_CAN_CAN) - is made sure here */
if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw))) if (memcmp(&gwj->ccgw, &ccgw, sizeof(ccgw)))