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 | |
| 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')
| -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 = { |
