blob: 25f504433ea431028a517aaa0eca241070cd0754 [file] [log] [blame]
Jorge Lucangeli Obesfc8ab532012-03-20 10:14:31 -07001/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include "bpf.h"
12
13/* Common jump targets. */
14#define NEXT 0
15#define SKIP 1
16#define SKIPN(_n) (_n)
17
18inline size_t set_bpf_instr(struct sock_filter *instr,
19 unsigned short code, unsigned int k,
20 unsigned char jt, unsigned char jf)
21{
22 instr->code = code;
23 instr->k = k;
24 instr->jt = jt;
25 instr->jf = jf;
26 return 1U;
27}
28
29/* Size-aware arg loaders. */
30#if defined(BITS32)
31size_t bpf_load_arg(struct sock_filter *filter, int argidx)
32{
33 set_bpf_stmt(filter, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
34 return 1U;
35}
36#elif defined(BITS64)
37size_t bpf_load_arg(struct sock_filter *filter, int argidx)
38{
39 struct sock_filter *curr_block = filter;
40 set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, LO_ARG(argidx));
41 set_bpf_stmt(curr_block++, BPF_ST, 0); /* lo -> M[0] */
42 set_bpf_stmt(curr_block++, BPF_LD+BPF_W+BPF_ABS, HI_ARG(argidx));
43 set_bpf_stmt(curr_block++, BPF_ST, 1); /* hi -> M[1] */
44 return curr_block - filter;
45}
46#endif
47
48/* Size-aware comparisons. */
49size_t bpf_comp_jeq32(struct sock_filter *filter, unsigned int c,
50 unsigned char jt, unsigned char jf)
51{
52 set_bpf_jump(filter, BPF_JMP+BPF_JEQ+BPF_K, c, jt, jf);
53 return 1U;
54}
55
56size_t bpf_comp_jeq64(struct sock_filter *filter, uint64_t c,
57 unsigned char jt, unsigned char jf)
58{
59 unsigned int lo = (unsigned int)(c & 0xFFFFFFFF);
60 unsigned int hi = (unsigned int)(c >> 32);
61
62 struct sock_filter *curr_block = filter;
63
64 /* bpf_load_arg leaves |hi| in A */
65 curr_block += bpf_comp_jeq32(curr_block, hi, NEXT, SKIPN(2) + jf);
66 set_bpf_stmt(curr_block++, BPF_LD+BPF_MEM, 0); /* swap in lo */
67 curr_block += bpf_comp_jeq32(curr_block, lo, jt, jf);
68
69 return curr_block - filter;
70}
71
72#if defined(BITS32)
73#define bpf_comp_jeq bpf_comp_jeq32
74
75#elif defined(BITS64)
76#define bpf_comp_jeq bpf_comp_jeq64
77#endif
78
79size_t bpf_arg_comp(struct sock_filter **pfilter,
80 int op, int argidx, unsigned long c, unsigned int label_id)
81{
82 struct sock_filter *filter = calloc(BPF_ARG_COMP_LEN + 1,
83 sizeof(struct sock_filter));
84 struct sock_filter *curr_block = filter;
85 int flip = 0;
86
87 /* Load arg */
88 curr_block += bpf_load_arg(curr_block, argidx);
89
90 /* Jump type */
91 switch (op) {
92 case EQ:
93 flip = 0;
94 break;
95 case NE:
96 flip = 1;
97 break;
98 default:
99 *pfilter = NULL;
100 return 0;
101 }
102
103 /*
104 * It's easier for the rest of the code to have the true branch
105 * skip and the false branch fall through.
106 */
107 unsigned char jt = flip ? NEXT : SKIP;
108 unsigned char jf = flip ? SKIP : NEXT;
109 curr_block += bpf_comp_jeq(curr_block, c, jt, jf);
110 curr_block += set_bpf_jump_lbl(curr_block, label_id);
111
112 *pfilter = filter;
113 return curr_block - filter;
114}
115
116void dump_bpf_filter(struct sock_filter *filter, unsigned short len)
117{
118 int i = 0;
119
120 printf("len == %d\n", len);
121 printf("filter:\n");
122 for (i = 0; i < len; i++) {
123 printf("%d: \t{ code=%#x, jt=%u, jf=%u, k=%#x \t}\n",
124 i, filter[i].code, filter[i].jt, filter[i].jf, filter[i].k);
125 }
126}
127
128void dump_bpf_prog(struct sock_fprog *fprog)
129{
130 struct sock_filter *filter = fprog->filter;
131 unsigned short len = fprog->len;
132 dump_bpf_filter(filter, len);
133}
134
135int bpf_resolve_jumps(struct bpf_labels *labels,
136 struct sock_filter *filter, size_t count)
137{
138 struct sock_filter *begin = filter;
139 __u8 insn = count - 1;
140
141 if (count < 1)
142 return -1;
143 /*
144 * Walk it once, backwards, to build the label table and do fixups.
145 * Since backward jumps are disallowed by BPF, this is easy.
146 */
147 for (filter += insn; filter >= begin; --insn, --filter) {
148 if (filter->code != (BPF_JMP+BPF_JA))
149 continue;
150 switch ((filter->jt<<8)|filter->jf) {
151 case (JUMP_JT<<8)|JUMP_JF:
152 if (labels->labels[filter->k].location == 0xffffffff) {
153 fprintf(stderr, "Unresolved label: '%s'\n",
154 labels->labels[filter->k].label);
155 return 1;
156 }
157 filter->k = labels->labels[filter->k].location -
158 (insn + 1);
159 filter->jt = 0;
160 filter->jf = 0;
161 continue;
162 case (LABEL_JT<<8)|LABEL_JF:
163 if (labels->labels[filter->k].location != 0xffffffff) {
164 fprintf(stderr, "Duplicate label use: '%s'\n",
165 labels->labels[filter->k].label);
166 return 1;
167 }
168 labels->labels[filter->k].location = insn;
169 filter->k = 0; /* fall through */
170 filter->jt = 0;
171 filter->jf = 0;
172 continue;
173 }
174 }
175 return 0;
176}
177
178/* Simple lookup table for labels. */
179int bpf_label_id(struct bpf_labels *labels, const char *label)
180{
181 struct __bpf_label *begin = labels->labels, *end;
182 int id;
183 if (labels->count == 0) {
184 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
185 if (!begin->label) {
186 return -1;
187 }
188 begin->location = 0xffffffff;
189 labels->count++;
190 return 0;
191 }
192 end = begin + labels->count;
193 for (id = 0; begin < end; ++begin, ++id) {
194 if (!strcmp(label, begin->label))
195 return id;
196 }
197 begin->label = strndup(label, MAX_BPF_LABEL_LEN);
198 if (!begin->label) {
199 return -1;
200 }
201 begin->location = 0xffffffff;
202 labels->count++;
203 return id;
204}
205
206/* Free label strings. */
207void free_label_strings(struct bpf_labels *labels)
208{
209 struct __bpf_label *begin = labels->labels, *end;
210
211 end = begin + labels->count;
212 for (; begin < end; ++begin) {
213 if (begin->label)
214 free((void*)(begin->label));
215 }
216}