1/* Shared library add-on to iptables to add u32 matching, 2 * generalized matching on values found at packet offsets 3 * 4 * Detailed doc is in the kernel module source 5 * net/ipv4/netfilter/ipt_u32.c 6 * 7 * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com> 8 * Released under the terms of GNU GPL v2 9 */ 10#include <stdio.h> 11#include <netdb.h> 12#include <string.h> 13#include <stdlib.h> 14#include <getopt.h> 15#include <iptables.h> 16#include <linux/netfilter_ipv4/ipt_u32.h> 17#include <errno.h> 18#include <ctype.h> 19 20/* Function which prints out usage message. */ 21static void 22help(void) 23{ 24 printf( "u32 v%s options:\n" 25 " --u32 tests\n" 26 " tests := location = value | tests && location = value\n" 27 " value := range | value , range\n" 28 " range := number | number : number\n" 29 " location := number | location operator number\n" 30 " operator := & | << | >> | @\n" 31 ,IPTABLES_VERSION); 32} 33 34/* defined in /usr/include/getopt.h maybe in man getopt */ 35static struct option opts[] = { 36 { "u32", 1, 0, '1' }, 37 { 0 } 38}; 39 40/* shared printing code */ 41static void print_u32(struct ipt_u32 *data) 42{ 43 unsigned int testind; 44 45 for (testind=0; testind < data->ntests; testind++) { 46 if (testind) printf("&&"); 47 { 48 unsigned int i; 49 50 printf("0x%x", data->tests[testind].location[0].number); 51 for (i = 1; i < data->tests[testind].nnums; i++) { 52 switch (data->tests[testind].location[i].nextop) { 53 case IPT_U32_AND: printf("&"); break; 54 case IPT_U32_LEFTSH: printf("<<"); break; 55 case IPT_U32_RIGHTSH: printf(">>"); break; 56 case IPT_U32_AT: printf("@"); break; 57 } 58 printf("0x%x", data->tests[testind].location[i].number); 59 } 60 printf("="); 61 for (i = 0; i < data->tests[testind].nvalues; i++) { 62 if (i) printf(","); 63 if (data->tests[testind].value[i].min 64 == data->tests[testind].value[i].max) 65 printf("0x%x", data->tests[testind].value[i].min); 66 else printf("0x%x:0x%x", data->tests[testind].value[i].min, 67 data->tests[testind].value[i].max); 68 } 69 } 70 } 71 printf(" "); 72} 73 74/* string_to_number is not quite what we need here ... */ 75u_int32_t parse_number(char **s, int pos) 76{ 77 u_int32_t number; 78 char *end; 79 errno = 0; 80 81 number = strtoul(*s, &end, 0); 82 if (end == *s) 83 exit_error(PARAMETER_PROBLEM, 84 "u32: at char %d expected number", pos); 85 if (errno) 86 exit_error(PARAMETER_PROBLEM, 87 "u32: at char %d error reading number", pos); 88 *s = end; 89 return number; 90} 91 92/* Function which parses command options; returns true if it ate an option */ 93static int 94parse(int c, char **argv, int invert, unsigned int *flags, 95 const struct ipt_entry *entry, 96 unsigned int *nfcache, 97 struct ipt_entry_match **match) 98{ 99 struct ipt_u32 *data = (struct ipt_u32 *)(*match)->data; 100 char *arg = argv[optind-1]; /* the argument string */ 101 char *start = arg; 102 int state=0, testind=0, locind=0, valind=0; 103 104 if (c != '1') return 0; 105 /* states: 0 = looking for numbers and operations, 1 = looking for ranges */ 106 while (1) { /* read next operand/number or range */ 107 while (isspace(*arg)) 108 arg++; /* skip white space */ 109 if (! *arg) { /* end of argument found */ 110 if (state == 0) 111 exit_error(PARAMETER_PROBLEM, 112 "u32: input ended in location spec"); 113 if (valind == 0) 114 exit_error(PARAMETER_PROBLEM, 115 "u32: test ended with no value spec"); 116 data->tests[testind].nnums = locind; 117 data->tests[testind].nvalues = valind; 118 testind++; 119 data->ntests=testind; 120 if (testind > U32MAXSIZE) 121 exit_error(PARAMETER_PROBLEM, 122 "u32: at char %d too many &&'s", 123 arg-start); 124 /* debugging 125 print_u32(data);printf("\n"); 126 exit_error(PARAMETER_PROBLEM, "debugging output done"); */ 127 return 1; 128 } 129 if (state == 0) { 130 /* reading location: read a number if nothing read yet, 131 otherwise either op number or = to end location spec */ 132 if (*arg == '=') { 133 if (locind == 0) 134 exit_error(PARAMETER_PROBLEM, 135 "u32: at char %d location spec missing", arg-start); 136 else { 137 arg++; 138 state=1; 139 } 140 } 141 else { 142 if (locind) { /* need op before number */ 143 if (*arg == '&') { 144 data->tests[testind].location[locind].nextop = IPT_U32_AND; 145 } 146 else if (*arg == '<') { 147 arg++; 148 if (*arg != '<') 149 exit_error(PARAMETER_PROBLEM, 150 "u32: at char %d a second < expected", arg-start); 151 data->tests[testind].location[locind].nextop = IPT_U32_LEFTSH; 152 } 153 else if (*arg == '>') { 154 arg++; 155 if (*arg != '>') 156 exit_error(PARAMETER_PROBLEM, 157 "u32: at char %d a second > expected", arg-start); 158 data->tests[testind].location[locind].nextop = IPT_U32_RIGHTSH; 159 } 160 else if (*arg == '@') { 161 data->tests[testind].location[locind].nextop = IPT_U32_AT; 162 } 163 else exit_error(PARAMETER_PROBLEM, 164 "u32: at char %d operator expected", arg-start); 165 arg++; 166 } 167 /* now a number; string_to_number skips white space? */ 168 data->tests[testind].location[locind].number = 169 parse_number(&arg, arg-start); 170 locind++; 171 if (locind > U32MAXSIZE) 172 exit_error(PARAMETER_PROBLEM, 173 "u32: at char %d too many operators", arg-start); 174 } 175 } 176 else { 177 /* state 1 - reading values: read a range if nothing read yet, 178 otherwise either ,range or && to end test spec */ 179 if (*arg == '&') { 180 arg++; 181 if (*arg != '&') 182 exit_error(PARAMETER_PROBLEM, 183 "u32: at char %d a second & expected", arg-start); 184 if (valind == 0) 185 exit_error(PARAMETER_PROBLEM, 186 "u32: at char %d value spec missing", arg-start); 187 else { 188 data->tests[testind].nnums = locind; 189 data->tests[testind].nvalues = valind; 190 testind++; 191 if (testind > U32MAXSIZE) 192 exit_error(PARAMETER_PROBLEM, 193 "u32: at char %d too many &&'s", arg-start); 194 arg++; state=0; locind=0; valind=0; 195 } 196 } 197 else { /* read value range */ 198 if (valind) { /* need , before number */ 199 if (*arg != ',') 200 exit_error(PARAMETER_PROBLEM, 201 "u32: at char %d expected , or &&", arg-start); 202 arg++; 203 } 204 data->tests[testind].value[valind].min = parse_number(&arg, arg-start); 205 while (isspace(*arg)) 206 arg++; /* another place white space could be */ 207 if (*arg==':') { 208 arg++; 209 data->tests[testind].value[valind].max 210 = parse_number(&arg, arg-start); 211 } 212 else data->tests[testind].value[valind].max 213 = data->tests[testind].value[valind].min; 214 valind++; 215 if (valind > U32MAXSIZE) 216 exit_error(PARAMETER_PROBLEM, 217 "u32: at char %d too many ,'s", arg-start); 218 } 219 } 220 } 221} 222 223/* Final check; must specify something. */ 224static void 225final_check(unsigned int flags) 226{ 227} 228 229/* Prints out the matchinfo. */ 230static void 231print(const struct ipt_ip *ip, 232 const struct ipt_entry_match *match, 233 int numeric) 234{ 235 printf("u32 "); 236 print_u32((struct ipt_u32 *)match->data); 237} 238 239/* Saves the union ipt_matchinfo in parsable form to stdout. */ 240static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) 241{ 242 printf("--u32 "); 243 print_u32((struct ipt_u32 *)match->data); 244} 245 246struct iptables_match u32 = { 247 .next = NULL, 248 .name = "u32", 249 .version = IPTABLES_VERSION, 250 .size = IPT_ALIGN(sizeof(struct ipt_u32)), 251 .userspacesize = IPT_ALIGN(sizeof(struct ipt_u32)), 252 .help = &help, 253 .parse = &parse, 254 .final_check = &final_check, 255 .print = &print, 256 .save = &save, 257 .extra_opts = opts 258}; 259 260void 261_init(void) 262{ 263 register_match(&u32); 264} 265