Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add LOG support. */
      2 #include <stdio.h>
      3 #include <netdb.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <syslog.h>
      7 #include <getopt.h>
      8 #include <iptables.h>
      9 #include <linux/netfilter_ipv4/ip_tables.h>
     10 #include <linux/netfilter_ipv4/ipt_LOG.h>
     11 
     12 #define LOG_DEFAULT_LEVEL LOG_WARNING
     13 
     14 #ifndef IPT_LOG_UID /* Old kernel */
     15 #define IPT_LOG_UID	0x08	/* Log UID owning local socket */
     16 #undef  IPT_LOG_MASK
     17 #define IPT_LOG_MASK	0x0f
     18 #endif
     19 
     20 /* Function which prints out usage message. */
     21 static void
     22 help(void)
     23 {
     24 	printf(
     25 "LOG v%s options:\n"
     26 " --log-level level		Level of logging (numeric or see syslog.conf)\n"
     27 " --log-prefix prefix		Prefix log messages with this prefix.\n\n"
     28 " --log-tcp-sequence		Log TCP sequence numbers.\n\n"
     29 " --log-tcp-options		Log TCP options.\n\n"
     30 " --log-ip-options		Log IP options.\n\n"
     31 " --log-uid			Log UID owning the local socket.\n\n",
     32 IPTABLES_VERSION);
     33 }
     34 
     35 static struct option opts[] = {
     36 	{ .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
     37 	{ .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
     38 	{ .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
     39 	{ .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
     40 	{ .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
     41 	{ .name = "log-uid",          .has_arg = 0, .flag = 0, .val = '4' },
     42 	{ .name = 0 }
     43 };
     44 
     45 /* Initialize the target. */
     46 static void
     47 init(struct ipt_entry_target *t, unsigned int *nfcache)
     48 {
     49 	struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data;
     50 
     51 	loginfo->level = LOG_DEFAULT_LEVEL;
     52 
     53 }
     54 
     55 struct ipt_log_names {
     56 	const char *name;
     57 	unsigned int level;
     58 };
     59 
     60 static struct ipt_log_names ipt_log_names[]
     61 = { { .name = "alert",   .level = LOG_ALERT },
     62     { .name = "crit",    .level = LOG_CRIT },
     63     { .name = "debug",   .level = LOG_DEBUG },
     64     { .name = "emerg",   .level = LOG_EMERG },
     65     { .name = "error",   .level = LOG_ERR },		/* DEPRECATED */
     66     { .name = "info",    .level = LOG_INFO },
     67     { .name = "notice",  .level = LOG_NOTICE },
     68     { .name = "panic",   .level = LOG_EMERG },		/* DEPRECATED */
     69     { .name = "warning", .level = LOG_WARNING }
     70 };
     71 
     72 static u_int8_t
     73 parse_level(const char *level)
     74 {
     75 	unsigned int lev = -1;
     76 	unsigned int set = 0;
     77 
     78 	if (string_to_number(level, 0, 7, &lev) == -1) {
     79 		unsigned int i = 0;
     80 
     81 		for (i = 0;
     82 		     i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
     83 		     i++) {
     84 			if (strncasecmp(level, ipt_log_names[i].name,
     85 					strlen(level)) == 0) {
     86 				if (set++)
     87 					exit_error(PARAMETER_PROBLEM,
     88 						   "log-level `%s' ambiguous",
     89 						   level);
     90 				lev = ipt_log_names[i].level;
     91 			}
     92 		}
     93 
     94 		if (!set)
     95 			exit_error(PARAMETER_PROBLEM,
     96 				   "log-level `%s' unknown", level);
     97 	}
     98 
     99 	return (u_int8_t)lev;
    100 }
    101 
    102 #define IPT_LOG_OPT_LEVEL 0x01
    103 #define IPT_LOG_OPT_PREFIX 0x02
    104 #define IPT_LOG_OPT_TCPSEQ 0x04
    105 #define IPT_LOG_OPT_TCPOPT 0x08
    106 #define IPT_LOG_OPT_IPOPT 0x10
    107 #define IPT_LOG_OPT_UID 0x20
    108 
    109 /* Function which parses command options; returns true if it
    110    ate an option */
    111 static int
    112 parse(int c, char **argv, int invert, unsigned int *flags,
    113       const struct ipt_entry *entry,
    114       struct ipt_entry_target **target)
    115 {
    116 	struct ipt_log_info *loginfo = (struct ipt_log_info *)(*target)->data;
    117 
    118 	switch (c) {
    119 	case '!':
    120 		if (*flags & IPT_LOG_OPT_LEVEL)
    121 			exit_error(PARAMETER_PROBLEM,
    122 				   "Can't specify --log-level twice");
    123 
    124 		if (check_inverse(optarg, &invert, NULL, 0))
    125 			exit_error(PARAMETER_PROBLEM,
    126 				   "Unexpected `!' after --log-level");
    127 
    128 		loginfo->level = parse_level(optarg);
    129 		*flags |= IPT_LOG_OPT_LEVEL;
    130 		break;
    131 
    132 	case '#':
    133 		if (*flags & IPT_LOG_OPT_PREFIX)
    134 			exit_error(PARAMETER_PROBLEM,
    135 				   "Can't specify --log-prefix twice");
    136 
    137 		if (check_inverse(optarg, &invert, NULL, 0))
    138 			exit_error(PARAMETER_PROBLEM,
    139 				   "Unexpected `!' after --log-prefix");
    140 
    141 		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
    142 			exit_error(PARAMETER_PROBLEM,
    143 				   "Maximum prefix length %u for --log-prefix",
    144 				   (unsigned int)sizeof(loginfo->prefix) - 1);
    145 
    146 		if (strlen(optarg) == 0)
    147 			exit_error(PARAMETER_PROBLEM,
    148 				   "No prefix specified for --log-prefix");
    149 
    150 		if (strlen(optarg) != strlen(strtok(optarg, "\n")))
    151 			exit_error(PARAMETER_PROBLEM,
    152 				   "Newlines not allowed in --log-prefix");
    153 
    154 		strcpy(loginfo->prefix, optarg);
    155 		*flags |= IPT_LOG_OPT_PREFIX;
    156 		break;
    157 
    158 	case '1':
    159 		if (*flags & IPT_LOG_OPT_TCPSEQ)
    160 			exit_error(PARAMETER_PROBLEM,
    161 				   "Can't specify --log-tcp-sequence "
    162 				   "twice");
    163 
    164 		loginfo->logflags |= IPT_LOG_TCPSEQ;
    165 		*flags |= IPT_LOG_OPT_TCPSEQ;
    166 		break;
    167 
    168 	case '2':
    169 		if (*flags & IPT_LOG_OPT_TCPOPT)
    170 			exit_error(PARAMETER_PROBLEM,
    171 				   "Can't specify --log-tcp-options twice");
    172 
    173 		loginfo->logflags |= IPT_LOG_TCPOPT;
    174 		*flags |= IPT_LOG_OPT_TCPOPT;
    175 		break;
    176 
    177 	case '3':
    178 		if (*flags & IPT_LOG_OPT_IPOPT)
    179 			exit_error(PARAMETER_PROBLEM,
    180 				   "Can't specify --log-ip-options twice");
    181 
    182 		loginfo->logflags |= IPT_LOG_IPOPT;
    183 		*flags |= IPT_LOG_OPT_IPOPT;
    184 		break;
    185 
    186 	case '4':
    187 		if (*flags & IPT_LOG_OPT_UID)
    188 			exit_error(PARAMETER_PROBLEM,
    189 				   "Can't specify --log-uid twice");
    190 
    191 		loginfo->logflags |= IPT_LOG_UID;
    192 		*flags |= IPT_LOG_OPT_UID;
    193 		break;
    194 
    195 	default:
    196 		return 0;
    197 	}
    198 
    199 	return 1;
    200 }
    201 
    202 /* Final check; nothing. */
    203 static void final_check(unsigned int flags)
    204 {
    205 }
    206 
    207 /* Prints out the targinfo. */
    208 static void
    209 print(const struct ipt_ip *ip,
    210       const struct ipt_entry_target *target,
    211       int numeric)
    212 {
    213 	const struct ipt_log_info *loginfo
    214 		= (const struct ipt_log_info *)target->data;
    215 	unsigned int i = 0;
    216 
    217 	printf("LOG ");
    218 	if (numeric)
    219 		printf("flags %u level %u ",
    220 		       loginfo->logflags, loginfo->level);
    221 	else {
    222 		for (i = 0;
    223 		     i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
    224 		     i++) {
    225 			if (loginfo->level == ipt_log_names[i].level) {
    226 				printf("level %s ", ipt_log_names[i].name);
    227 				break;
    228 			}
    229 		}
    230 		if (i == sizeof(ipt_log_names) / sizeof(struct ipt_log_names))
    231 			printf("UNKNOWN level %u ", loginfo->level);
    232 		if (loginfo->logflags & IPT_LOG_TCPSEQ)
    233 			printf("tcp-sequence ");
    234 		if (loginfo->logflags & IPT_LOG_TCPOPT)
    235 			printf("tcp-options ");
    236 		if (loginfo->logflags & IPT_LOG_IPOPT)
    237 			printf("ip-options ");
    238 		if (loginfo->logflags & IPT_LOG_UID)
    239 			printf("uid ");
    240 		if (loginfo->logflags & ~(IPT_LOG_MASK))
    241 			printf("unknown-flags ");
    242 	}
    243 
    244 	if (strcmp(loginfo->prefix, "") != 0)
    245 		printf("prefix `%s' ", loginfo->prefix);
    246 }
    247 
    248 /* Saves the union ipt_targinfo in parsable form to stdout. */
    249 static void
    250 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
    251 {
    252 	const struct ipt_log_info *loginfo
    253 		= (const struct ipt_log_info *)target->data;
    254 
    255 	if (strcmp(loginfo->prefix, "") != 0)
    256 		printf("--log-prefix \"%s\" ", loginfo->prefix);
    257 
    258 	if (loginfo->level != LOG_DEFAULT_LEVEL)
    259 		printf("--log-level %d ", loginfo->level);
    260 
    261 	if (loginfo->logflags & IPT_LOG_TCPSEQ)
    262 		printf("--log-tcp-sequence ");
    263 	if (loginfo->logflags & IPT_LOG_TCPOPT)
    264 		printf("--log-tcp-options ");
    265 	if (loginfo->logflags & IPT_LOG_IPOPT)
    266 		printf("--log-ip-options ");
    267 	if (loginfo->logflags & IPT_LOG_UID)
    268 		printf("--log-uid ");
    269 }
    270 
    271 static
    272 struct iptables_target log
    273 = {
    274     .name          = "LOG",
    275     .version       = IPTABLES_VERSION,
    276     .size          = IPT_ALIGN(sizeof(struct ipt_log_info)),
    277     .userspacesize = IPT_ALIGN(sizeof(struct ipt_log_info)),
    278     .help          = &help,
    279     .init          = &init,
    280     .parse         = &parse,
    281     .final_check   = &final_check,
    282     .print         = &print,
    283     .save          = &save,
    284     .extra_opts    = opts
    285 };
    286 
    287 void ipt_LOG_init(void)
    288 {
    289 	register_target(&log);
    290 }
    291