#define CMD_NEW_CHAIN 0x0100U
#define CMD_DELETE_CHAIN 0x0200U
#define CMD_SET_POLICY 0x0400U
-#define CMD_CHECK 0x0800U
-#define CMD_RENAME_CHAIN 0x1000U
+#define CMD_RENAME_CHAIN 0x0800U
#define NUMBER_OF_CMD 13
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
'N', 'X', 'P', 'E' };
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* -n -s -d -p -j -v -x -i -o --line */
-/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
-/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
-/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x'},
-/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
-/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
-/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' '},
-/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x'},
-/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x'},
-/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
-/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
-/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x'},
-/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ','x'},
-/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x'}
+ /* -n -s -d -p -j -v -x -i -o --line -c */
+/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
+/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' ','x'},
+/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'}
};
static int inverse_for_options[NUMBER_OF_OPT] =
/* -x */ 0,
/* -i */ IP6T_INV_VIA_IN,
/* -o */ IP6T_INV_VIA_OUT,
-/*--line*/ 0
+/*--line*/ 0,
+/* -c */ 0,
};
const char *program_version;
const char *program_name;
char *lib_dir;
+/* the path to command to load kernel module */
+const char *modprobe = NULL;
+
/* Keeping track of external matches and targets: linked lists. */
struct ip6tables_match *ip6tables_matches = NULL;
struct ip6tables_target *ip6tables_targets = NULL;
#define IPPROTO_AH 51
#endif
#endif
+#ifndef IPPROTO_MH
+#define IPPROTO_MH 135
+#endif
static const struct pprot chain_protos[] = {
{ "tcp", IPPROTO_TCP },
{ "udp", IPPROTO_UDP },
+ { "udplite", IPPROTO_UDPLITE },
{ "icmpv6", IPPROTO_ICMPV6 },
+ { "ipv6-icmp", IPPROTO_ICMPV6 },
{ "esp", IPPROTO_ESP },
{ "ah", IPPROTO_AH },
+ { "ipv6-mh", IPPROTO_MH },
+ { "mh", IPPROTO_MH },
{ "all", 0 },
};
return NULL;
}
+int
+service_to_port(const char *name, const char *proto)
+{
+ struct servent *service;
+
+ if ((service = getservbyname(name, proto)) != NULL)
+ return ntohs((unsigned short) service->s_port);
+
+ return -1;
+}
+
+u_int16_t
+parse_port(const char *port, const char *proto)
+{
+ unsigned int portnum;
+
+ if ((string_to_number(port, 0, 65535, &portnum)) != -1 ||
+ (portnum = service_to_port(port, proto)) != -1)
+ return (u_int16_t)portnum;
+
+ exit_error(PARAMETER_PROBLEM,
+ "invalid port/service `%s' specified", port);
+}
+
static void
in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
{
}
struct ip6tables_match *
-find_match(const char *name, enum ip6t_tryload tryload, struct ip6tables_rule_match **matches)
+find_match(const char *match_name, enum ip6t_tryload tryload, struct ip6tables_rule_match **matches)
{
struct ip6tables_match *ptr;
- int icmphack = 0;
+ const char *icmp6 = "icmp6";
+ const char *name;
/* This is ugly as hell. Nonetheless, there is no way of changing
* this without hurting backwards compatibility */
- if ( (strcmp(name,"icmpv6") == 0) ||
- (strcmp(name,"ipv6-icmp") == 0) ||
- (strcmp(name,"icmp6") == 0) ) icmphack = 1;
+ if ( (strcmp(match_name,"icmpv6") == 0) ||
+ (strcmp(match_name,"ipv6-icmp") == 0) ||
+ (strcmp(match_name,"icmp6") == 0) )
+ name = icmp6;
+ else
+ name = match_name;
- if (!icmphack) {
- for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
- if (strcmp(name, ptr->name) == 0)
- break;
- }
- } else {
- for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
- if (strcmp("icmp6", ptr->name) == 0)
- break;
- }
- }
+ for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+ if (strcmp(name, ptr->name) == 0) {
+ struct ip6tables_match *clone;
+
+ /* First match of this type: */
+ if (ptr->m == NULL)
+ break;
+
+ /* Second and subsequent clones */
+ clone = fw_malloc(sizeof(struct ip6tables_match));
+ memcpy(clone, ptr, sizeof(struct ip6tables_match));
+ clone->mflags = 0;
+ /* This is a clone: */
+ clone->next = clone;
+
+ ptr = clone;
+ break;
+ }
+ }
#ifndef NO_SHARED_LIBS
if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
char path[strlen(lib_dir) + sizeof("/libip6t_.so")
+ strlen(name)];
- if (!icmphack)
- sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
- else
- sprintf(path, "%s/libip6t_%s.so", lib_dir, "icmpv6");
+ sprintf(path, "%s/libip6t_%s.so", lib_dir, name);
if (dlopen(path, RTLD_NOW)) {
/* Found library. If it didn't register itself,
maybe they specified target as match. */
newentry = fw_malloc(sizeof(struct ip6tables_rule_match));
- for (i = matches; *i; i = &(*i)->next);
+ for (i = matches; *i; i = &(*i)->next) {
+ if (strcmp(name, (*i)->match->name) == 0)
+ (*i)->completed = 1;
+ }
newentry->match = ptr;
+ newentry->completed = 0;
newentry->next = NULL;
*i = newentry;
}
if (string_to_number(s, 0, 255, &proto) == -1) {
struct protoent *pent;
+ /* first deal with the special case of 'all' to prevent
+ * people from being able to redefine 'all' in nsswitch
+ * and/or provoke expensive [not working] ldap/nis/...
+ * lookups */
+ if (!strcmp(s, "all"))
+ return 0;
+
if ((pent = getprotobyname(s)))
proto = pent->p_proto;
else {
return (u_int16_t)proto;
}
+/* These are invalid numbers as upper layer protocol */
+static int is_exthdr(u_int16_t proto)
+{
+ return (proto == IPPROTO_ROUTING ||
+ proto == IPPROTO_FRAGMENT ||
+ proto == IPPROTO_AH ||
+ proto == IPPROTO_DSTOPTS);
+}
+
void parse_interface(const char *arg, char *vianame, unsigned char *mask)
{
int vialen = strlen(arg);
memset(mask, 0xFF, vialen + 1);
memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
for (i = 0; vianame[i]; i++) {
- if (!isalnum(vianame[i])
- && vianame[i] != '_'
- && vianame[i] != '.') {
- printf("Warning: wierd character in interface"
+ if (vianame[i] == ':' ||
+ vianame[i] == '!' ||
+ vianame[i] == '*') {
+ printf("Warning: weird character in interface"
" `%s' (No aliases, :, ! or *).\n",
vianame);
break;
return merge;
}
+static int compatible_revision(const char *name, u_int8_t revision, int opt)
+{
+ struct ip6t_get_revision rev;
+ socklen_t s = sizeof(rev);
+ int max_rev, sockfd;
+
+ sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+ if (sockfd < 0) {
+ fprintf(stderr, "Could not open socket to kernel: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+
+ strcpy(rev.name, name);
+ rev.revision = revision;
+
+ load_ip6tables_ko(modprobe, 1);
+
+ max_rev = getsockopt(sockfd, IPPROTO_IPV6, opt, &rev, &s);
+ if (max_rev < 0) {
+ /* Definitely don't support this? */
+ if (errno == EPROTONOSUPPORT) {
+ close(sockfd);
+ return 0;
+ } else if (errno == ENOPROTOOPT) {
+ close(sockfd);
+ /* Assume only revision 0 support (old kernel) */
+ return (revision == 0);
+ } else {
+ fprintf(stderr, "getsockopt failed strangely: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+ close(sockfd);
+ return 1;
+}
+
+static int compatible_match_revision(const char *name, u_int8_t revision)
+{
+ return compatible_revision(name, revision, IP6T_SO_GET_REVISION_MATCH);
+}
+
void
register_match6(struct ip6tables_match *me)
{
- struct ip6tables_match **i;
+ struct ip6tables_match **i, *old;
if (strcmp(me->version, program_version) != 0) {
fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
exit(1);
}
- if (find_match(me->name, DURING_LOAD, NULL)) {
- fprintf(stderr, "%s: match `%s' already registered.\n",
+ /* Revision field stole a char from name. */
+ if (strlen(me->name) >= IP6T_FUNCTION_MAXNAMELEN-1) {
+ fprintf(stderr, "%s: target `%s' has invalid name\n",
program_name, me->name);
exit(1);
}
+ old = find_match(me->name, DURING_LOAD, NULL);
+ if (old) {
+ if (old->revision == me->revision) {
+ fprintf(stderr,
+ "%s: match `%s' already registered.\n",
+ program_name, me->name);
+ exit(1);
+ }
+
+ /* Now we have two (or more) options, check compatibility. */
+ if (compatible_match_revision(old->name, old->revision)
+ && old->revision > me->revision)
+ return;
+
+ /* Replace if compatible. */
+ if (!compatible_match_revision(me->name, me->revision))
+ return;
+
+ /* Delete old one. */
+ for (i = &ip6tables_matches; *i!=old; i = &(*i)->next);
+ *i = old->next;
+ }
+
if (me->size != IP6T_ALIGN(me->size)) {
fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
program_name, me->name, (unsigned int)me->size);
return NULL;
}
-int ip6tables_insmod(const char *modname, const char *modprobe)
+int ip6tables_insmod(const char *modname, const char *modprobe, int quiet)
{
char *buf = NULL;
- char *argv[3];
+ char *argv[4];
int status;
/* If they don't explicitly set it, read out of kernel */
case 0:
argv[0] = (char *)modprobe;
argv[1] = (char *)modname;
- argv[2] = NULL;
+ if (quiet) {
+ argv[2] = "-q";
+ argv[3] = NULL;
+ } else {
+ argv[2] = NULL;
+ argv[3] = NULL;
+ }
execv(argv[0], argv);
/* not usually reached */
return -1;
}
+int load_ip6tables_ko(const char *modprobe, int quiet)
+{
+ static int loaded = 0;
+ static int ret = -1;
+
+ if (!loaded) {
+ ret = ip6tables_insmod("ip6_tables", modprobe, quiet);
+ loaded = (ret == 0);
+ }
+
+ return ret;
+}
+
static struct ip6t_entry *
generate_entry(const struct ip6t_entry *fw,
struct ip6tables_rule_match *matches,
for (matchp = *matches; matchp;) {
tmp = matchp->next;
- if (matchp->match->m)
+ if (matchp->match->m) {
free(matchp->match->m);
+ matchp->match->m = NULL;
+ }
+ if (matchp->match == matchp->match->next) {
+ free(matchp->match);
+ matchp->match = NULL;
+ }
free(matchp);
matchp = tmp;
}
*matches = NULL;
}
+static void set_revision(char *name, u_int8_t revision)
+{
+ /* Old kernel sources don't have ".revision" field,
+ but we stole a byte from name. */
+ name[IP6T_FUNCTION_MAXNAMELEN - 2] = '\0';
+ name[IP6T_FUNCTION_MAXNAMELEN - 1] = revision;
+}
+
int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
{
struct ip6t_entry fw, *e = NULL;
struct ip6tables_target *t;
const char *jumpto = "";
char *protocol = NULL;
- const char *modprobe = NULL;
int proto_used = 0;
- char icmp6p[] = "icmpv6";
memset(&fw, 0, sizeof(fw));
newname = argv[optind++];
else
exit_error(PARAMETER_PROBLEM,
- "-%c requires old-chain-name and "
+ "-%c requires old-chain-name and "
"new-chain-name",
cmd2char(CMD_RENAME_CHAIN));
break;
*protocol = tolower(*protocol);
protocol = argv[optind-1];
- if ( strcmp(protocol,"ipv6-icmp") == 0)
- protocol = icmp6p;
fw.ipv6.proto = parse_protocol(protocol);
fw.ipv6.flags |= IP6T_F_PROTO;
&& (fw.ipv6.invflags & IP6T_INV_PROTO))
exit_error(PARAMETER_PROBLEM,
"rule would never match protocol");
+
+ if (is_exthdr(fw.ipv6.proto)
+ && (fw.ipv6.invflags & IP6T_INV_PROTO) == 0)
+ printf("Warning: never matched protocol: %s. "
+ "use extension match instead.\n",
+ protocol);
break;
case 's':
m->m = fw_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
+ set_revision(m->m->u.user.name, m->revision);
if (m->init != NULL)
m->init(m->m, &fw.nfcache);
- opts = merge_options(opts, m->extra_opts, &m->option_offset);
+ if (m != m->next)
+ /* Merge options for non-cloned matches */
+ opts = merge_options(opts, m->extra_opts, &m->option_offset);
}
break;
exit_tryhelp(2);
default:
- /* FIXME: This scheme doesn't allow two of the same
- matches --RR */
if (!target
|| !(target->parse(c - target->option_offset,
argv, invert,
&target->tflags,
&fw, &target->t))) {
for (matchp = matches; matchp; matchp = matchp->next) {
+ if (matchp->completed)
+ continue;
if (matchp->match->parse(c - matchp->match->option_offset,
argv, invert,
&matchp->match->mflags,
actually hear this code suck. */
/* some explanations (after four different bugs
- * in 3 different releases): If we encountere a
+ * in 3 different releases): If we encounter a
* parameter, that has not been parsed yet,
* it's not an option of an explicitly loaded
* match or a target. However, we support
m->m = fw_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
+ set_revision(m->m->u.user.name,
+ m->revision);
if (m->init != NULL)
m->init(m->m, &fw.nfcache);
*handle = ip6tc_init(*table);
/* try to insmod the module if iptc_init failed */
- if (!*handle && ip6tables_insmod("ip6_tables", modprobe) != -1)
+ if (!*handle && load_ip6tables_ko(modprobe, 0) != -1)
*handle = ip6tc_init(*table);
if (!*handle)