1/* Shared library add-on to iptables to add TIME matching support. */ 2#include <stdio.h> 3#include <netdb.h> 4#include <string.h> 5#include <stdlib.h> 6#include <stddef.h> /* for 'offsetof' */ 7#include <getopt.h> 8 9//#include <iptables.h> 10#include <ip6tables.h> 11#include <linux/netfilter_ipv4/ipt_time.h> 12#include <time.h> 13 14static int globaldays; 15 16/* Function which prints out usage message. */ 17static void 18help(void) 19{ 20 printf( 21"TIME v%s options:\n" 22" [ --timestart value ] [ --timestop value] [ --days listofdays ] [ --datestart value ] [ --datestop value ]\n" 23" timestart value : HH:MM (default 00:00)\n" 24" timestop value : HH:MM (default 23:59)\n" 25" Note: daylight savings time changes are not tracked\n" 26" listofdays value: a list of days to apply\n" 27" from Mon,Tue,Wed,Thu,Fri,Sat,Sun\n" 28" Coma speparated, no space, case sensitive.\n" 29" Defaults to all days.\n" 30" datestart value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n" 31" If any of month, day, hour, minute or second is\n" 32" not specified, then defaults to their smallest\n" 33" 1900 <= YYYY < 2037\n" 34" 1 <= MM <= 12\n" 35" 1 <= DD <= 31\n" 36" 0 <= hh <= 23\n" 37" 0 <= mm <= 59\n" 38" 0 <= ss <= 59\n" 39" datestop value : YYYY[:MM[:DD[:hh[:mm[:ss]]]]]\n" 40" If the whole option is ommited, default to never stop\n" 41" If any of month, day, hour, minute or second is\n" 42" not specified, then default to their smallest\n", 43IPTABLES_VERSION); 44} 45 46static struct option opts[] = { 47 { "timestart", 1, 0, '1' }, 48 { "timestop", 1, 0, '2' }, 49 { "days", 1, 0, '3'}, 50 { "datestart", 1, 0, '4' }, 51 { "datestop", 1, 0, '5' }, 52 {0} 53}; 54 55/* Initialize the match. */ 56static void 57init(struct ip6t_entry_match *m, unsigned int *nfcache) 58{ 59 struct ipt_time_info *info = (struct ipt_time_info *)m->data; 60 globaldays = 0; 61 /* By default, we match on everyday */ 62 info->days_match = 127; 63 /* By default, we match on every hour:min of the day */ 64 info->time_start = 0; 65 info->time_stop = 1439; /* (23*60+59 = 1439 */ 66 /* By default, we don't have any date-begin or date-end boundaries */ 67 info->date_start = 0; 68 info->date_stop = LONG_MAX; 69} 70 71/** 72 * param: part1, a pointer on a string 2 chars maximum long string, that will contain the hours. 73 * param: part2, a pointer on a string 2 chars maximum long string, that will contain the minutes. 74 * param: str_2_parse, the string to parse. 75 * return: 1 if ok, 0 if error. 76 */ 77static int 78split_time(char **part1, char **part2, const char *str_2_parse) 79{ 80 unsigned short int i,j=0; 81 char *rpart1 = *part1; 82 char *rpart2 = *part2; 83 unsigned char found_column = 0; 84 85 /* Check the length of the string */ 86 if (strlen(str_2_parse) > 5) 87 return 0; 88 /* parse the first part until the ':' */ 89 for (i=0; i<2; i++) 90 { 91 if (str_2_parse[i] == ':') 92 found_column = 1; 93 else 94 rpart1[i] = str_2_parse[i]; 95 } 96 if (!found_column) 97 i++; 98 j=i; 99 /* parse the second part */ 100 for (; i<strlen(str_2_parse); i++) 101 { 102 rpart2[i-j] = str_2_parse[i]; 103 } 104 /* if we are here, format should be ok. */ 105 return 1; 106} 107 108static int 109parse_number(char *str, int num_min, int num_max, int *number) 110{ 111 /* if the number starts with 0, replace it with a space else 112 string_to_number() will interpret it as octal !! */ 113 if (strlen(str) == 0) 114 return 0; 115 116 if ((str[0] == '0') && (str[1] != '\0')) 117 str[0] = ' '; 118 119 return string_to_number(str, num_min, num_max, number); 120} 121 122static void 123parse_time_string(int *hour, int *minute, const char *time) 124{ 125 char *hours; 126 char *minutes; 127 hours = (char *)malloc(3); 128 minutes = (char *)malloc(3); 129 memset(hours, 0, 3); 130 memset(minutes, 0, 3); 131 132 if (split_time((char **)&hours, (char **)&minutes, time) == 1) 133 { 134 *hour = 0; 135 *minute = 0; 136 if ((parse_number((char *)hours, 0, 23, hour) != -1) && 137 (parse_number((char *)minutes, 0, 59, minute) != -1)) 138 { 139 free(hours); 140 free(minutes); 141 return; 142 } 143 } 144 145 free(hours); 146 free(minutes); 147 148 /* If we are here, there was a problem ..*/ 149 exit_error(PARAMETER_PROBLEM, 150 "invalid time `%s' specified, should be HH:MM format", time); 151} 152 153/* return 1->ok, return 0->error */ 154static int 155parse_day(int *days, int from, int to, const char *string) 156{ 157 char *dayread; 158 char *days_str[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 159 unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; 160 unsigned int i; 161 162 dayread = (char *)malloc(4); 163 bzero(dayread, 4); 164 if ((to-from) != 3) { 165 free(dayread); 166 return 0; 167 } 168 for (i=from; i<to; i++) 169 dayread[i-from] = string[i]; 170 for (i=0; i<7; i++) 171 if (strcmp(dayread, days_str[i]) == 0) 172 { 173 *days |= days_of_week[i]; 174 free(dayread); 175 return 1; 176 } 177 /* if we are here, we didn't read a valid day */ 178 free(dayread); 179 return 0; 180} 181 182static void 183parse_days_string(int *days, const char *daystring) 184{ 185 int len; 186 int i=0; 187 char *err = "invalid days `%s' specified, should be Sun,Mon,Tue... format"; 188 189 len = strlen(daystring); 190 if (len < 3) 191 exit_error(PARAMETER_PROBLEM, err, daystring); 192 while(i<len) 193 { 194 if (parse_day(days, i, i+3, daystring) == 0) 195 exit_error(PARAMETER_PROBLEM, err, daystring); 196 i += 4; 197 } 198} 199 200static int 201parse_date_field(const char *str_to_parse, int str_to_parse_s, int start_pos, 202 char *dest, int *next_pos) 203{ 204 unsigned char found_value = 0; 205 unsigned char found_column = 0; 206 int i; 207 208 for (i=0; i<2; i++) 209 { 210 if ((i+start_pos) >= str_to_parse_s) /* don't exit boundaries of the string.. */ 211 break; 212 if (str_to_parse[i+start_pos] == ':') 213 found_column = 1; 214 else 215 { 216 found_value = 1; 217 dest[i] = str_to_parse[i+start_pos]; 218 } 219 } 220 if (found_value == 0) 221 return 0; 222 *next_pos = i + start_pos; 223 if (found_column == 0) 224 ++(*next_pos); 225 return 1; 226} 227 228static int 229split_date(char *year, char *month, char *day, 230 char *hour, char *minute, char *second, 231 const char *str_to_parse) 232{ 233 int i; 234 unsigned char found_column = 0; 235 int str_to_parse_s = strlen(str_to_parse); 236 237 /* Check the length of the string */ 238 if ((str_to_parse_s > 19) || /* YYYY:MM:DD:HH:MM:SS */ 239 (str_to_parse_s < 4)) /* YYYY*/ 240 return 0; 241 242 /* Clear the buffers */ 243 memset(year, 0, 4); 244 memset(month, 0, 2); 245 memset(day, 0, 2); 246 memset(hour, 0, 2); 247 memset(minute, 0, 2); 248 memset(second, 0, 2); 249 250 /* parse the year YYYY */ 251 found_column = 0; 252 for (i=0; i<5; i++) 253 { 254 if (i >= str_to_parse_s) 255 break; 256 if (str_to_parse[i] == ':') 257 { 258 found_column = 1; 259 break; 260 } 261 else 262 year[i] = str_to_parse[i]; 263 } 264 if (found_column == 1) 265 ++i; 266 267 /* parse the month if it exists */ 268 if (! parse_date_field(str_to_parse, str_to_parse_s, i, month, &i)) 269 return 1; 270 271 if (! parse_date_field(str_to_parse, str_to_parse_s, i, day, &i)) 272 return 1; 273 274 if (! parse_date_field(str_to_parse, str_to_parse_s, i, hour, &i)) 275 return 1; 276 277 if (! parse_date_field(str_to_parse, str_to_parse_s, i, minute, &i)) 278 return 1; 279 280 parse_date_field(str_to_parse, str_to_parse_s, i, second, &i); 281 282 /* if we are here, format should be ok. */ 283 return 1; 284} 285 286static time_t 287parse_date_string(const char *str_to_parse) 288{ 289 char year[5]; 290 char month[3]; 291 char day[3]; 292 char hour[3]; 293 char minute[3]; 294 char second[3]; 295 struct tm t; 296 time_t temp_time; 297 298 memset(year, 0, 5); 299 memset(month, 0, 3); 300 memset(day, 0, 3); 301 memset(hour, 0, 3); 302 memset(minute, 0, 3); 303 memset(second, 0, 3); 304 305 if (split_date(year, month, day, hour, minute, second, str_to_parse) == 1) 306 { 307 memset((void *)&t, 0, sizeof(struct tm)); 308 t.tm_isdst = 0; 309 t.tm_mday = 1; 310 if (!((parse_number(year, 1900, 2037, &(t.tm_year)) == -1) || 311 (parse_number(month, 1, 12, &(t.tm_mon)) == -1) || 312 (parse_number(day, 1, 31, &(t.tm_mday)) == -1) || 313 (parse_number(hour, 0, 9999, &(t.tm_hour)) == -1) || 314 (parse_number(minute, 0, 59, &(t.tm_min)) == -1) || 315 (parse_number(second, 0, 59, &(t.tm_sec)) == -1))) 316 { 317 t.tm_year -= 1900; 318 --(t.tm_mon); 319 temp_time = mktime(&t); 320 if (temp_time != -1) 321 return temp_time; 322 } 323 } 324 exit_error(PARAMETER_PROBLEM, 325 "invalid date `%s' specified, should be YYYY[:MM[:DD[:hh[:mm[:ss]]]]] format", str_to_parse); 326} 327 328#define IPT_TIME_START 0x01 329#define IPT_TIME_STOP 0x02 330#define IPT_TIME_DAYS 0x04 331#define IPT_DATE_START 0x08 332#define IPT_DATE_STOP 0x10 333 334/* Function which parses command options; returns true if it 335 ate an option */ 336static int 337parse(int c, char **argv, int invert, unsigned int *flags, 338 const struct ip6t_entry *entry, 339 unsigned int *nfcache, 340 struct ip6t_entry_match **match) 341{ 342 struct ipt_time_info *timeinfo = (struct ipt_time_info *)(*match)->data; 343 int hours, minutes; 344 time_t temp_date; 345 346 switch (c) 347 { 348 /* timestart */ 349 case '1': 350 if (invert) 351 exit_error(PARAMETER_PROBLEM, 352 "unexpected '!' with --timestart"); 353 if (*flags & IPT_TIME_START) 354 exit_error(PARAMETER_PROBLEM, 355 "Can't specify --timestart twice"); 356 parse_time_string(&hours, &minutes, optarg); 357 timeinfo->time_start = (hours * 60) + minutes; 358 *flags |= IPT_TIME_START; 359 break; 360 /* timestop */ 361 case '2': 362 if (invert) 363 exit_error(PARAMETER_PROBLEM, 364 "unexpected '!' with --timestop"); 365 if (*flags & IPT_TIME_STOP) 366 exit_error(PARAMETER_PROBLEM, 367 "Can't specify --timestop twice"); 368 parse_time_string(&hours, &minutes, optarg); 369 timeinfo->time_stop = (hours * 60) + minutes; 370 *flags |= IPT_TIME_STOP; 371 break; 372 373 /* days */ 374 case '3': 375 if (invert) 376 exit_error(PARAMETER_PROBLEM, 377 "unexpected '!' with --days"); 378 if (*flags & IPT_TIME_DAYS) 379 exit_error(PARAMETER_PROBLEM, 380 "Can't specify --days twice"); 381 parse_days_string(&globaldays, optarg); 382 timeinfo->days_match = globaldays; 383 *flags |= IPT_TIME_DAYS; 384 break; 385 386 /* datestart */ 387 case '4': 388 if (invert) 389 exit_error(PARAMETER_PROBLEM, 390 "unexpected '!' with --datestart"); 391 if (*flags & IPT_DATE_START) 392 exit_error(PARAMETER_PROBLEM, 393 "Can't specify --datestart twice"); 394 temp_date = parse_date_string(optarg); 395 timeinfo->date_start = temp_date; 396 *flags |= IPT_DATE_START; 397 break; 398 399 /* datestop*/ 400 case '5': 401 if (invert) 402 exit_error(PARAMETER_PROBLEM, 403 "unexpected '!' with --datestop"); 404 if (*flags & IPT_DATE_STOP) 405 exit_error(PARAMETER_PROBLEM, 406 "Can't specify --datestop twice"); 407 temp_date = parse_date_string(optarg); 408 timeinfo->date_stop = temp_date; 409 *flags |= IPT_DATE_STOP; 410 break; 411 default: 412 return 0; 413 } 414 return 1; 415} 416 417/* Final check */ 418static void 419final_check(unsigned int flags) 420{ 421 /* Nothing to do */ 422} 423 424 425static void 426print_days(int daynum) 427{ 428 char *days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 429 unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; 430 unsigned short int i, nbdays=0; 431 432 for (i=0; i<7; i++) { 433 if ((days_of_week[i] & daynum) == days_of_week[i]) 434 { 435 if (nbdays>0) 436 printf(",%s", days[i]); 437 else 438 printf("%s", days[i]); 439 ++nbdays; 440 } 441 } 442 printf(" "); 443} 444 445static void 446divide_time(int fulltime, int *hours, int *minutes) 447{ 448 *hours = fulltime / 60; 449 *minutes = fulltime % 60; 450} 451 452static void 453print_date(time_t date, char *command) 454{ 455 struct tm *t; 456 457 /* If it's default value, don't print..*/ 458 if (((date == 0) || (date == LONG_MAX)) && (command != NULL)) 459 return; 460 t = localtime(&date); 461 if (command != NULL) 462 printf("%s %d:%d:%d:%d:%d:%d ", command, (t->tm_year + 1900), (t->tm_mon + 1), 463 t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 464 else 465 printf("%d-%d-%d %d:%d:%d ", (t->tm_year + 1900), (t->tm_mon + 1), 466 t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); 467} 468 469/* Prints out the matchinfo. */ 470static void 471print(const struct ip6t_ip6 *ip, 472 const struct ip6t_entry_match *match, 473 int numeric) 474{ 475 struct ipt_time_info *time = ((struct ipt_time_info *)match->data); 476 int hour_start, hour_stop, minute_start, minute_stop; 477 478 divide_time(time->time_start, &hour_start, &minute_start); 479 divide_time(time->time_stop, &hour_stop, &minute_stop); 480 printf("TIME "); 481 if (time->time_start != 0) 482 printf("from %d:%d ", hour_start, minute_start); 483 if (time->time_stop != 1439) /* 23*60+59 = 1439 */ 484 printf("to %d:%d ", hour_stop, minute_stop); 485 printf("on "); 486 if (time->days_match == 127) 487 printf("all days "); 488 else 489 print_days(time->days_match); 490 if (time->date_start != 0) 491 { 492 printf("starting from "); 493 print_date(time->date_start, NULL); 494 } 495 if (time->date_stop != LONG_MAX) 496 { 497 printf("until date "); 498 print_date(time->date_stop, NULL); 499 } 500} 501 502/* Saves the data in parsable form to stdout. */ 503static void 504save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match) 505{ 506 struct ipt_time_info *time = ((struct ipt_time_info *)match->data); 507 int hour_start, hour_stop, minute_start, minute_stop; 508 509 divide_time(time->time_start, &hour_start, &minute_start); 510 divide_time(time->time_stop, &hour_stop, &minute_stop); 511 if (time->time_start != 0) 512 printf("--timestart %.2d:%.2d ", 513 hour_start, minute_start); 514 515 if (time->time_stop != 1439) /* 23*60+59 = 1439 */ 516 printf("--timestop %.2d:%.2d ", 517 hour_stop, minute_stop); 518 519 if (time->days_match != 127) 520 { 521 printf("--days "); 522 print_days(time->days_match); 523 printf(" "); 524 } 525 print_date(time->date_start, "--datestart"); 526 print_date(time->date_stop, "--datestop"); 527} 528 529/* have to use offsetof() instead of IPT_ALIGN(), since kerneltime must not 530 * be compared when user deletes rule with '-D' */ 531static 532struct ip6tables_match timestruct = { 533 .next = NULL, 534 .name = "time", 535 .version = IPTABLES_VERSION, 536 .size = IP6T_ALIGN(sizeof(struct ipt_time_info)), 537 .userspacesize = offsetof(struct ipt_time_info, kerneltime), 538 .help = &help, 539 .init = &init, 540 .parse = &parse, 541 .final_check = &final_check, 542 .print = &print, 543 .save = &save, 544 .extra_opts = opts 545}; 546 547void _init(void) 548{ 549 register_match6(×truct); 550} 551