diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2015-03-13 14:57:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-15 22:02:28 -0400 |
commit | 9bac3d6d548e5cc925570b263f35b70a00a00ffd (patch) | |
tree | 00ae5c37fb877d7c2b4fdf5ee0b8fe4837b1d476 /net/core | |
parent | a498cfe990f569dd3766bfc9bfd2a5ee04468cd2 (diff) |
bpf: allow extended BPF programs access skb fields
introduce user accessible mirror of in-kernel 'struct sk_buff':
struct __sk_buff {
__u32 len;
__u32 pkt_type;
__u32 mark;
__u32 queue_mapping;
};
bpf programs can do:
int bpf_prog(struct __sk_buff *skb)
{
__u32 var = skb->pkt_type;
which will be compiled to bpf assembler as:
dst_reg = *(u32 *)(src_reg + 4) // 4 == offsetof(struct __sk_buff, pkt_type)
bpf verifier will check validity of access and will convert it to:
dst_reg = *(u8 *)(src_reg + offsetof(struct sk_buff, __pkt_type_offset))
dst_reg &= 7
since skb->pkt_type is a bitfield.
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/filter.c | 100 |
1 files changed, 83 insertions, 17 deletions
diff --git a/net/core/filter.c b/net/core/filter.c index 33310eee6134..4e9dd0ad0d5b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -150,10 +150,43 @@ static u64 __get_random_u32(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) | |||
150 | return prandom_u32(); | 150 | return prandom_u32(); |
151 | } | 151 | } |
152 | 152 | ||
153 | static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg, | ||
154 | struct bpf_insn *insn_buf) | ||
155 | { | ||
156 | struct bpf_insn *insn = insn_buf; | ||
157 | |||
158 | switch (skb_field) { | ||
159 | case SKF_AD_MARK: | ||
160 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); | ||
161 | |||
162 | *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, | ||
163 | offsetof(struct sk_buff, mark)); | ||
164 | break; | ||
165 | |||
166 | case SKF_AD_PKTTYPE: | ||
167 | *insn++ = BPF_LDX_MEM(BPF_B, dst_reg, src_reg, PKT_TYPE_OFFSET()); | ||
168 | *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, PKT_TYPE_MAX); | ||
169 | #ifdef __BIG_ENDIAN_BITFIELD | ||
170 | *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 5); | ||
171 | #endif | ||
172 | break; | ||
173 | |||
174 | case SKF_AD_QUEUE: | ||
175 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); | ||
176 | |||
177 | *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg, | ||
178 | offsetof(struct sk_buff, queue_mapping)); | ||
179 | break; | ||
180 | } | ||
181 | |||
182 | return insn - insn_buf; | ||
183 | } | ||
184 | |||
153 | static bool convert_bpf_extensions(struct sock_filter *fp, | 185 | static bool convert_bpf_extensions(struct sock_filter *fp, |
154 | struct bpf_insn **insnp) | 186 | struct bpf_insn **insnp) |
155 | { | 187 | { |
156 | struct bpf_insn *insn = *insnp; | 188 | struct bpf_insn *insn = *insnp; |
189 | u32 cnt; | ||
157 | 190 | ||
158 | switch (fp->k) { | 191 | switch (fp->k) { |
159 | case SKF_AD_OFF + SKF_AD_PROTOCOL: | 192 | case SKF_AD_OFF + SKF_AD_PROTOCOL: |
@@ -167,13 +200,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, | |||
167 | break; | 200 | break; |
168 | 201 | ||
169 | case SKF_AD_OFF + SKF_AD_PKTTYPE: | 202 | case SKF_AD_OFF + SKF_AD_PKTTYPE: |
170 | *insn++ = BPF_LDX_MEM(BPF_B, BPF_REG_A, BPF_REG_CTX, | 203 | cnt = convert_skb_access(SKF_AD_PKTTYPE, BPF_REG_A, BPF_REG_CTX, insn); |
171 | PKT_TYPE_OFFSET()); | 204 | insn += cnt - 1; |
172 | *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, PKT_TYPE_MAX); | ||
173 | #ifdef __BIG_ENDIAN_BITFIELD | ||
174 | insn++; | ||
175 | *insn = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 5); | ||
176 | #endif | ||
177 | break; | 205 | break; |
178 | 206 | ||
179 | case SKF_AD_OFF + SKF_AD_IFINDEX: | 207 | case SKF_AD_OFF + SKF_AD_IFINDEX: |
@@ -197,10 +225,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, | |||
197 | break; | 225 | break; |
198 | 226 | ||
199 | case SKF_AD_OFF + SKF_AD_MARK: | 227 | case SKF_AD_OFF + SKF_AD_MARK: |
200 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); | 228 | cnt = convert_skb_access(SKF_AD_MARK, BPF_REG_A, BPF_REG_CTX, insn); |
201 | 229 | insn += cnt - 1; | |
202 | *insn = BPF_LDX_MEM(BPF_W, BPF_REG_A, BPF_REG_CTX, | ||
203 | offsetof(struct sk_buff, mark)); | ||
204 | break; | 230 | break; |
205 | 231 | ||
206 | case SKF_AD_OFF + SKF_AD_RXHASH: | 232 | case SKF_AD_OFF + SKF_AD_RXHASH: |
@@ -211,10 +237,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, | |||
211 | break; | 237 | break; |
212 | 238 | ||
213 | case SKF_AD_OFF + SKF_AD_QUEUE: | 239 | case SKF_AD_OFF + SKF_AD_QUEUE: |
214 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2); | 240 | cnt = convert_skb_access(SKF_AD_QUEUE, BPF_REG_A, BPF_REG_CTX, insn); |
215 | 241 | insn += cnt - 1; | |
216 | *insn = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX, | ||
217 | offsetof(struct sk_buff, queue_mapping)); | ||
218 | break; | 242 | break; |
219 | 243 | ||
220 | case SKF_AD_OFF + SKF_AD_VLAN_TAG: | 244 | case SKF_AD_OFF + SKF_AD_VLAN_TAG: |
@@ -1151,13 +1175,55 @@ sk_filter_func_proto(enum bpf_func_id func_id) | |||
1151 | static bool sk_filter_is_valid_access(int off, int size, | 1175 | static bool sk_filter_is_valid_access(int off, int size, |
1152 | enum bpf_access_type type) | 1176 | enum bpf_access_type type) |
1153 | { | 1177 | { |
1154 | /* skb fields cannot be accessed yet */ | 1178 | /* only read is allowed */ |
1155 | return false; | 1179 | if (type != BPF_READ) |
1180 | return false; | ||
1181 | |||
1182 | /* check bounds */ | ||
1183 | if (off < 0 || off >= sizeof(struct __sk_buff)) | ||
1184 | return false; | ||
1185 | |||
1186 | /* disallow misaligned access */ | ||
1187 | if (off % size != 0) | ||
1188 | return false; | ||
1189 | |||
1190 | /* all __sk_buff fields are __u32 */ | ||
1191 | if (size != 4) | ||
1192 | return false; | ||
1193 | |||
1194 | return true; | ||
1195 | } | ||
1196 | |||
1197 | static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off, | ||
1198 | struct bpf_insn *insn_buf) | ||
1199 | { | ||
1200 | struct bpf_insn *insn = insn_buf; | ||
1201 | |||
1202 | switch (ctx_off) { | ||
1203 | case offsetof(struct __sk_buff, len): | ||
1204 | BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); | ||
1205 | |||
1206 | *insn++ = BPF_LDX_MEM(BPF_W, dst_reg, src_reg, | ||
1207 | offsetof(struct sk_buff, len)); | ||
1208 | break; | ||
1209 | |||
1210 | case offsetof(struct __sk_buff, mark): | ||
1211 | return convert_skb_access(SKF_AD_MARK, dst_reg, src_reg, insn); | ||
1212 | |||
1213 | case offsetof(struct __sk_buff, pkt_type): | ||
1214 | return convert_skb_access(SKF_AD_PKTTYPE, dst_reg, src_reg, insn); | ||
1215 | |||
1216 | case offsetof(struct __sk_buff, queue_mapping): | ||
1217 | return convert_skb_access(SKF_AD_QUEUE, dst_reg, src_reg, insn); | ||
1218 | } | ||
1219 | |||
1220 | return insn - insn_buf; | ||
1156 | } | 1221 | } |
1157 | 1222 | ||
1158 | static const struct bpf_verifier_ops sk_filter_ops = { | 1223 | static const struct bpf_verifier_ops sk_filter_ops = { |
1159 | .get_func_proto = sk_filter_func_proto, | 1224 | .get_func_proto = sk_filter_func_proto, |
1160 | .is_valid_access = sk_filter_is_valid_access, | 1225 | .is_valid_access = sk_filter_is_valid_access, |
1226 | .convert_ctx_access = sk_filter_convert_ctx_access, | ||
1161 | }; | 1227 | }; |
1162 | 1228 | ||
1163 | static struct bpf_prog_type_list sk_filter_type __read_mostly = { | 1229 | static struct bpf_prog_type_list sk_filter_type __read_mostly = { |