aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@plumgrid.com>2015-03-13 14:57:42 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-15 22:02:28 -0400
commit9bac3d6d548e5cc925570b263f35b70a00a00ffd (patch)
tree00ae5c37fb877d7c2b4fdf5ee0b8fe4837b1d476 /net/core
parenta498cfe990f569dd3766bfc9bfd2a5ee04468cd2 (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.c100
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
153static 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
153static bool convert_bpf_extensions(struct sock_filter *fp, 185static 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)
1151static bool sk_filter_is_valid_access(int off, int size, 1175static 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
1197static 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
1158static const struct bpf_verifier_ops sk_filter_ops = { 1223static 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
1163static struct bpf_prog_type_list sk_filter_type __read_mostly = { 1229static struct bpf_prog_type_list sk_filter_type __read_mostly = {