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