diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2015-03-26 22:53:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 16:26:54 -0400 |
commit | 608cd71a9c7c9db76e78a792c5a4101e12fea43f (patch) | |
tree | 9824e0795f923a6370e53799f84e8ad13764cb23 | |
parent | 7836b16c0dd1d3039ccc35ceeff6d75401fbba8c (diff) |
tc: bpf: generalize pedit action
existing TC action 'pedit' can munge any bits of the packet.
Generalize it for use in bpf programs attached as cls_bpf and act_bpf via
bpf_skb_store_bytes() helper function.
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Reviewed-by: Jiri Pirko <jiri@resnulli.us>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/bpf.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/bpf.h | 1 | ||||
-rw-r--r-- | kernel/bpf/verifier.c | 2 | ||||
-rw-r--r-- | net/core/filter.c | 71 |
4 files changed, 73 insertions, 2 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 280a315de8d6..d5cda067115a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h | |||
@@ -59,6 +59,7 @@ enum bpf_arg_type { | |||
59 | ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */ | 59 | ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */ |
60 | ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */ | 60 | ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */ |
61 | 61 | ||
62 | ARG_PTR_TO_CTX, /* pointer to context */ | ||
62 | ARG_ANYTHING, /* any (initialized) argument is ok */ | 63 | ARG_ANYTHING, /* any (initialized) argument is ok */ |
63 | }; | 64 | }; |
64 | 65 | ||
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 27dc4ec58840..74aab6e0d964 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h | |||
@@ -168,6 +168,7 @@ enum bpf_func_id { | |||
168 | BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ | 168 | BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */ |
169 | BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ | 169 | BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */ |
170 | BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ | 170 | BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */ |
171 | BPF_FUNC_skb_store_bytes, /* int skb_store_bytes(skb, offset, from, len) */ | ||
171 | __BPF_FUNC_MAX_ID, | 172 | __BPF_FUNC_MAX_ID, |
172 | }; | 173 | }; |
173 | 174 | ||
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 0e714f799ec0..630a7bac1e51 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -773,6 +773,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno, | |||
773 | expected_type = CONST_IMM; | 773 | expected_type = CONST_IMM; |
774 | } else if (arg_type == ARG_CONST_MAP_PTR) { | 774 | } else if (arg_type == ARG_CONST_MAP_PTR) { |
775 | expected_type = CONST_PTR_TO_MAP; | 775 | expected_type = CONST_PTR_TO_MAP; |
776 | } else if (arg_type == ARG_PTR_TO_CTX) { | ||
777 | expected_type = PTR_TO_CTX; | ||
776 | } else { | 778 | } else { |
777 | verbose("unsupported arg_type %d\n", arg_type); | 779 | verbose("unsupported arg_type %d\n", arg_type); |
778 | return -EFAULT; | 780 | return -EFAULT; |
diff --git a/net/core/filter.c b/net/core/filter.c index 32f43c59908c..444a07e4f68d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -1175,6 +1175,56 @@ int sk_attach_bpf(u32 ufd, struct sock *sk) | |||
1175 | return 0; | 1175 | return 0; |
1176 | } | 1176 | } |
1177 | 1177 | ||
1178 | static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5) | ||
1179 | { | ||
1180 | struct sk_buff *skb = (struct sk_buff *) (long) r1; | ||
1181 | unsigned int offset = (unsigned int) r2; | ||
1182 | void *from = (void *) (long) r3; | ||
1183 | unsigned int len = (unsigned int) r4; | ||
1184 | char buf[16]; | ||
1185 | void *ptr; | ||
1186 | |||
1187 | /* bpf verifier guarantees that: | ||
1188 | * 'from' pointer points to bpf program stack | ||
1189 | * 'len' bytes of it were initialized | ||
1190 | * 'len' > 0 | ||
1191 | * 'skb' is a valid pointer to 'struct sk_buff' | ||
1192 | * | ||
1193 | * so check for invalid 'offset' and too large 'len' | ||
1194 | */ | ||
1195 | if (offset > 0xffff || len > sizeof(buf)) | ||
1196 | return -EFAULT; | ||
1197 | |||
1198 | if (skb_cloned(skb) && !skb_clone_writable(skb, offset + len)) | ||
1199 | return -EFAULT; | ||
1200 | |||
1201 | ptr = skb_header_pointer(skb, offset, len, buf); | ||
1202 | if (unlikely(!ptr)) | ||
1203 | return -EFAULT; | ||
1204 | |||
1205 | skb_postpull_rcsum(skb, ptr, len); | ||
1206 | |||
1207 | memcpy(ptr, from, len); | ||
1208 | |||
1209 | if (ptr == buf) | ||
1210 | /* skb_store_bits cannot return -EFAULT here */ | ||
1211 | skb_store_bits(skb, offset, ptr, len); | ||
1212 | |||
1213 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
1214 | skb->csum = csum_add(skb->csum, csum_partial(ptr, len, 0)); | ||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | const struct bpf_func_proto bpf_skb_store_bytes_proto = { | ||
1219 | .func = bpf_skb_store_bytes, | ||
1220 | .gpl_only = false, | ||
1221 | .ret_type = RET_INTEGER, | ||
1222 | .arg1_type = ARG_PTR_TO_CTX, | ||
1223 | .arg2_type = ARG_ANYTHING, | ||
1224 | .arg3_type = ARG_PTR_TO_STACK, | ||
1225 | .arg4_type = ARG_CONST_STACK_SIZE, | ||
1226 | }; | ||
1227 | |||
1178 | static const struct bpf_func_proto * | 1228 | static const struct bpf_func_proto * |
1179 | sk_filter_func_proto(enum bpf_func_id func_id) | 1229 | sk_filter_func_proto(enum bpf_func_id func_id) |
1180 | { | 1230 | { |
@@ -1194,6 +1244,17 @@ sk_filter_func_proto(enum bpf_func_id func_id) | |||
1194 | } | 1244 | } |
1195 | } | 1245 | } |
1196 | 1246 | ||
1247 | static const struct bpf_func_proto * | ||
1248 | tc_cls_act_func_proto(enum bpf_func_id func_id) | ||
1249 | { | ||
1250 | switch (func_id) { | ||
1251 | case BPF_FUNC_skb_store_bytes: | ||
1252 | return &bpf_skb_store_bytes_proto; | ||
1253 | default: | ||
1254 | return sk_filter_func_proto(func_id); | ||
1255 | } | ||
1256 | } | ||
1257 | |||
1197 | static bool sk_filter_is_valid_access(int off, int size, | 1258 | static bool sk_filter_is_valid_access(int off, int size, |
1198 | enum bpf_access_type type) | 1259 | enum bpf_access_type type) |
1199 | { | 1260 | { |
@@ -1270,18 +1331,24 @@ static const struct bpf_verifier_ops sk_filter_ops = { | |||
1270 | .convert_ctx_access = sk_filter_convert_ctx_access, | 1331 | .convert_ctx_access = sk_filter_convert_ctx_access, |
1271 | }; | 1332 | }; |
1272 | 1333 | ||
1334 | static const struct bpf_verifier_ops tc_cls_act_ops = { | ||
1335 | .get_func_proto = tc_cls_act_func_proto, | ||
1336 | .is_valid_access = sk_filter_is_valid_access, | ||
1337 | .convert_ctx_access = sk_filter_convert_ctx_access, | ||
1338 | }; | ||
1339 | |||
1273 | static struct bpf_prog_type_list sk_filter_type __read_mostly = { | 1340 | static struct bpf_prog_type_list sk_filter_type __read_mostly = { |
1274 | .ops = &sk_filter_ops, | 1341 | .ops = &sk_filter_ops, |
1275 | .type = BPF_PROG_TYPE_SOCKET_FILTER, | 1342 | .type = BPF_PROG_TYPE_SOCKET_FILTER, |
1276 | }; | 1343 | }; |
1277 | 1344 | ||
1278 | static struct bpf_prog_type_list sched_cls_type __read_mostly = { | 1345 | static struct bpf_prog_type_list sched_cls_type __read_mostly = { |
1279 | .ops = &sk_filter_ops, | 1346 | .ops = &tc_cls_act_ops, |
1280 | .type = BPF_PROG_TYPE_SCHED_CLS, | 1347 | .type = BPF_PROG_TYPE_SCHED_CLS, |
1281 | }; | 1348 | }; |
1282 | 1349 | ||
1283 | static struct bpf_prog_type_list sched_act_type __read_mostly = { | 1350 | static struct bpf_prog_type_list sched_act_type __read_mostly = { |
1284 | .ops = &sk_filter_ops, | 1351 | .ops = &tc_cls_act_ops, |
1285 | .type = BPF_PROG_TYPE_SCHED_ACT, | 1352 | .type = BPF_PROG_TYPE_SCHED_ACT, |
1286 | }; | 1353 | }; |
1287 | 1354 | ||