1 /* 2 * Xtables BPF extension 3 * 4 * Written by Willem de Bruijn (willemb (at) google.com) 5 * Copyright Google, Inc. 2013 6 * Licensed under the GNU General Public License version 2 (GPLv2) 7 */ 8 9 #include <linux/netfilter/xt_bpf.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <unistd.h> 18 #include <xtables.h> 19 #include "config.h" 20 21 #ifdef HAVE_LINUX_BPF_H 22 #include <linux/bpf.h> 23 #endif 24 25 #include <linux/unistd.h> 26 27 #define BCODE_FILE_MAX_LEN_B 1024 28 29 enum { 30 O_BCODE_STDIN = 0, 31 O_OBJ_PINNED = 1, 32 }; 33 34 static void bpf_help(void) 35 { 36 printf( 37 "bpf match options:\n" 38 "--bytecode <program> : a bpf program as generated by\n" 39 " $(nfbpf_compile RAW '<filter>')\n"); 40 } 41 42 static void bpf_help_v1(void) 43 { 44 printf( 45 "bpf match options:\n" 46 "--bytecode <program> : a bpf program as generated by\n" 47 " $(nfbpf_compile RAW '<filter>')\n" 48 "--object-pinned <bpf object> : a path to a pinned BPF object in bpf fs\n"); 49 } 50 51 static const struct xt_option_entry bpf_opts[] = { 52 {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING}, 53 XTOPT_TABLEEND, 54 }; 55 56 static const struct xt_option_entry bpf_opts_v1[] = { 57 {.name = "bytecode", .id = O_BCODE_STDIN, .type = XTTYPE_STRING}, 58 {.name = "object-pinned" , .id = O_OBJ_PINNED, .type = XTTYPE_STRING, 59 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_bpf_info_v1, path)}, 60 XTOPT_TABLEEND, 61 }; 62 63 static int bpf_obj_get(const char *filepath) 64 { 65 #if defined HAVE_LINUX_BPF_H && defined __NR_bpf 66 union bpf_attr attr; 67 68 memset(&attr, 0, sizeof(attr)); 69 attr.pathname = (__u64) filepath; 70 71 return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); 72 #else 73 xtables_error(OTHER_PROBLEM, 74 "No bpf header, kernel headers too old?\n"); 75 return -EINVAL; 76 #endif 77 } 78 79 static void bpf_parse_string(struct sock_filter *pc, __u16 *lenp, __u16 len_max, 80 const char *bpf_program) 81 { 82 const char separator = ','; 83 const char *token; 84 char sp; 85 int i; 86 __u16 len; 87 88 /* parse head: length. */ 89 if (sscanf(bpf_program, "%hu%c", &len, &sp) != 2 || 90 sp != separator) 91 xtables_error(PARAMETER_PROBLEM, 92 "bpf: error parsing program length"); 93 if (!len) 94 xtables_error(PARAMETER_PROBLEM, 95 "bpf: illegal zero length program"); 96 if (len > len_max) 97 xtables_error(PARAMETER_PROBLEM, 98 "bpf: number of instructions exceeds maximum"); 99 100 /* parse instructions. */ 101 i = 0; 102 token = bpf_program; 103 while ((token = strchr(token, separator)) && (++token)[0]) { 104 if (i >= len) 105 xtables_error(PARAMETER_PROBLEM, 106 "bpf: real program length exceeds" 107 " the encoded length parameter"); 108 if (sscanf(token, "%hu %hhu %hhu %u,", 109 &pc->code, &pc->jt, &pc->jf, &pc->k) != 4) 110 xtables_error(PARAMETER_PROBLEM, 111 "bpf: error at instr %d", i); 112 i++; 113 pc++; 114 } 115 116 if (i != len) 117 xtables_error(PARAMETER_PROBLEM, 118 "bpf: parsed program length is less than the" 119 " encoded length parameter"); 120 121 *lenp = len; 122 } 123 124 static void bpf_parse_obj_pinned(struct xt_bpf_info_v1 *bi, 125 const char *filepath) 126 { 127 bi->fd = bpf_obj_get(filepath); 128 if (bi->fd < 0) 129 xtables_error(PARAMETER_PROBLEM, 130 "bpf: failed to get bpf object"); 131 132 /* Cannot close bi->fd explicitly. Rely on exit */ 133 if (fcntl(bi->fd, F_SETFD, FD_CLOEXEC) == -1) { 134 xtables_error(OTHER_PROBLEM, 135 "Could not set close on exec: %s\n", 136 strerror(errno)); 137 } 138 } 139 140 static void bpf_parse(struct xt_option_call *cb) 141 { 142 struct xt_bpf_info *bi = (void *) cb->data; 143 144 xtables_option_parse(cb); 145 switch (cb->entry->id) { 146 case O_BCODE_STDIN: 147 bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem, 148 ARRAY_SIZE(bi->bpf_program), cb->arg); 149 break; 150 default: 151 xtables_error(PARAMETER_PROBLEM, "bpf: unknown option"); 152 } 153 } 154 155 static void bpf_parse_v1(struct xt_option_call *cb) 156 { 157 struct xt_bpf_info_v1 *bi = (void *) cb->data; 158 159 xtables_option_parse(cb); 160 switch (cb->entry->id) { 161 case O_BCODE_STDIN: 162 bpf_parse_string(bi->bpf_program, &bi->bpf_program_num_elem, 163 ARRAY_SIZE(bi->bpf_program), cb->arg); 164 bi->mode = XT_BPF_MODE_BYTECODE; 165 break; 166 case O_OBJ_PINNED: 167 bpf_parse_obj_pinned(bi, cb->arg); 168 bi->mode = XT_BPF_MODE_FD_PINNED; 169 break; 170 default: 171 xtables_error(PARAMETER_PROBLEM, "bpf: unknown option"); 172 } 173 } 174 175 static void bpf_print_code(const struct sock_filter *pc, __u16 len, char tail) 176 { 177 for (; len; len--, pc++) 178 printf("%hu %hhu %hhu %u%c", 179 pc->code, pc->jt, pc->jf, pc->k, 180 len > 1 ? ',' : tail); 181 } 182 183 static void bpf_save_code(const struct sock_filter *pc, __u16 len) 184 { 185 printf(" --bytecode \"%hu,", len); 186 bpf_print_code(pc, len, '\"'); 187 } 188 189 static void bpf_save(const void *ip, const struct xt_entry_match *match) 190 { 191 const struct xt_bpf_info *info = (void *) match->data; 192 193 bpf_save_code(info->bpf_program, info->bpf_program_num_elem); 194 } 195 196 static void bpf_save_v1(const void *ip, const struct xt_entry_match *match) 197 { 198 const struct xt_bpf_info_v1 *info = (void *) match->data; 199 200 if (info->mode == XT_BPF_MODE_BYTECODE) 201 bpf_save_code(info->bpf_program, info->bpf_program_num_elem); 202 else if (info->mode == XT_BPF_MODE_FD_PINNED) 203 printf(" --object-pinned %s", info->path); 204 else 205 xtables_error(OTHER_PROBLEM, "unknown bpf mode"); 206 } 207 208 static void bpf_fcheck(struct xt_fcheck_call *cb) 209 { 210 if (!(cb->xflags & (1 << O_BCODE_STDIN))) 211 xtables_error(PARAMETER_PROBLEM, 212 "bpf: missing --bytecode parameter"); 213 } 214 215 static void bpf_fcheck_v1(struct xt_fcheck_call *cb) 216 { 217 const unsigned int bit_bcode = 1 << O_BCODE_STDIN; 218 const unsigned int bit_pinned = 1 << O_OBJ_PINNED; 219 unsigned int flags; 220 221 flags = cb->xflags & (bit_bcode | bit_pinned); 222 if (flags != bit_bcode && flags != bit_pinned) 223 xtables_error(PARAMETER_PROBLEM, 224 "bpf: one of --bytecode or --pinned is required"); 225 } 226 227 static void bpf_print(const void *ip, const struct xt_entry_match *match, 228 int numeric) 229 { 230 const struct xt_bpf_info *info = (void *) match->data; 231 232 printf("match bpf "); 233 bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0'); 234 } 235 236 static void bpf_print_v1(const void *ip, const struct xt_entry_match *match, 237 int numeric) 238 { 239 const struct xt_bpf_info_v1 *info = (void *) match->data; 240 241 printf("match bpf "); 242 if (info->mode == XT_BPF_MODE_BYTECODE) 243 bpf_print_code(info->bpf_program, info->bpf_program_num_elem, '\0'); 244 else if (info->mode == XT_BPF_MODE_FD_PINNED) 245 printf("pinned %s", info->path); 246 else 247 printf("unknown"); 248 } 249 250 static struct xtables_match bpf_matches[] = { 251 { 252 .family = NFPROTO_UNSPEC, 253 .name = "bpf", 254 .version = XTABLES_VERSION, 255 .revision = 0, 256 .size = XT_ALIGN(sizeof(struct xt_bpf_info)), 257 .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info, filter)), 258 .help = bpf_help, 259 .print = bpf_print, 260 .save = bpf_save, 261 .x6_parse = bpf_parse, 262 .x6_fcheck = bpf_fcheck, 263 .x6_options = bpf_opts, 264 }, 265 { 266 .family = NFPROTO_UNSPEC, 267 .name = "bpf", 268 .version = XTABLES_VERSION, 269 .revision = 1, 270 .size = XT_ALIGN(sizeof(struct xt_bpf_info_v1)), 271 .userspacesize = XT_ALIGN(offsetof(struct xt_bpf_info_v1, filter)), 272 .help = bpf_help_v1, 273 .print = bpf_print_v1, 274 .save = bpf_save_v1, 275 .x6_parse = bpf_parse_v1, 276 .x6_fcheck = bpf_fcheck_v1, 277 .x6_options = bpf_opts_v1, 278 }, 279 }; 280 281 void _init(void) 282 { 283 xtables_register_matches(bpf_matches, ARRAY_SIZE(bpf_matches)); 284 } 285