/** * ipset_parse_iptimeout - parse IPv4|IPv6 address and timeout * @session: session structure * @opt: option kind of the data * @str: string to parse * * Parse string as an IPv4|IPv6 address and timeout parameter. * If family is not set yet in the data blob, INET is assumed. * The value is stored in the data blob of the session. * * Compatibility parser. * * Returns 0 on success or a negative error code. */ int ipset_parse_iptimeout(struct ipset_session *session, enum ipset_opt opt, const char *str) { char *tmp, *saved, *a; int err; assert(session); assert(opt == IPSET_OPT_IP); assert(str); /* IP,timeout */ if (ipset_data_flags_test(ipset_session_data(session), IPSET_FLAG(IPSET_OPT_TIMEOUT))) return syntax_err("mixed syntax, timeout already specified"); tmp = saved = strdup(str); if (saved == NULL) return ipset_err(session, "Cannot allocate memory to duplicate %s.", str); a = elem_separator(tmp); if (a == NULL) { free(saved); return syntax_err("Missing separator from %s", str); } *a++ = '\0'; err = parse_ip(session, opt, tmp, IPADDR_ANY); if (!err) err = ipset_parse_uint32(session, IPSET_OPT_TIMEOUT, a); free(saved); return err; }
/** * ipset_parse_family - parse INET|INET6 family names * @session: session structure * @opt: option kind of the data * @str: string to parse * * Parse string as an INET|INET6 family name. * The value is stored in the data blob of the session. * * Returns 0 on success or a negative error code. */ int ipset_parse_family(struct ipset_session *session, enum ipset_opt opt, const char *str) { struct ipset_data *data; uint8_t family; assert(session); assert(opt == IPSET_OPT_FAMILY); assert(str); data = ipset_session_data(session); if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FAMILY))) syntax_err("protocol family may not be specified " "multiple times"); if (STREQ(str, "inet") || STREQ(str, "ipv4") || STREQ(str, "-4")) family = AF_INET; else if (STREQ(str, "inet6") || STREQ(str, "ipv6") || STREQ(str, "-6")) family = AF_INET6; else if (STREQ(str, "any") || STREQ(str, "unspec")) family = AF_UNSPEC; else return syntax_err("unknown INET family %s", str); return ipset_data_set(data, opt, &family); }
/** * ipset_parse_name_compat - parse setname as element * @session: session structure * @opt: option kind of the data * @str: string to parse * * Parse string as a setname or a setname element to add to a set. * The pattern "setname,before|after,setname" is recognized and * parsed. * The value is stored in the data blob of the session. * * Returns 0 on success or a negative error code. */ int ipset_parse_name_compat(struct ipset_session *session, enum ipset_opt opt, const char *str) { char *saved; char *a = NULL, *b = NULL, *tmp; int err, before = 0; const char *sep = IPSET_ELEM_SEPARATOR; struct ipset_data *data; assert(session); assert(opt == IPSET_OPT_NAME); assert(str); data = ipset_session_data(session); if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) syntax_err("mixed syntax, before|after option already used"); tmp = saved = strdup(str); if (saved == NULL) return ipset_err(session, "Cannot allocate memory to duplicate %s.", str); if ((a = elem_separator(tmp)) != NULL) { /* setname,[before|after,setname */ *a++ = '\0'; if ((b = elem_separator(a)) != NULL) *b++ = '\0'; if (b == NULL || !(STREQ(a, "before") || STREQ(a, "after"))) { err = ipset_err(session, "you must specify elements " "as setname%s[before|after]%ssetname", sep, sep); goto out; } before = STREQ(a, "before"); } check_setname(tmp, saved); if ((err = ipset_data_set(data, opt, tmp)) != 0 || b == NULL) goto out; check_setname(b, saved); if ((err = ipset_data_set(data, IPSET_OPT_NAMEREF, b)) != 0) goto out; if (before) err = ipset_data_set(data, IPSET_OPT_BEFORE, &before); out: free(saved); return err; }
/** * ipset_print_proto_port - print proto:port * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print protocol and port to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_proto_port(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt ASSERT_UNUSED, uint8_t env UNUSED) { int size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT2); if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) { uint8_t proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env); SNPRINTF_FAILURE(size, len, offset); if (len < 2) return -ENOSPC; size = snprintf(buf + offset, len, IPSET_PROTO_SEPARATOR); SNPRINTF_FAILURE(size, len, offset); switch (proto) { case IPPROTO_TCP: case IPPROTO_SCTP: case IPPROTO_UDP: case IPPROTO_UDPLITE: break; case IPPROTO_ICMP: size = ipset_print_icmp(buf + offset, len, data, opt, env); goto out; case IPPROTO_ICMPV6: size = ipset_print_icmpv6(buf + offset, len, data, opt, env); goto out; default: break; } } size = ipset_print_port(buf + offset, len, data, opt, env); out: SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_parse_after - parse string as "after" reference setname * @session: session structure * @opt: option kind of the data * @str: string to parse * * Parse string as a "after" reference setname for list:set * type of sets. The value is stored in the data blob of the session. * * Returns 0 on success or a negative error code. */ int ipset_parse_after(struct ipset_session *session, enum ipset_opt opt, const char *str) { struct ipset_data *data; assert(session); assert(opt == IPSET_OPT_NAMEREF); assert(str); data = ipset_session_data(session); if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_NAMEREF))) syntax_err("mixed syntax, before|after option already used"); check_setname(str, NULL); return ipset_data_set(data, opt, str); }
/** * ipset_print_name - print setname element string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print setname element string to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_name(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { const char *name; int size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_NAME); if (len < 2*IPSET_MAXNAMELEN + 2 + strlen("before")) return -1; name = ipset_data_get(data, opt); assert(name); size = snprintf(buf, len, "%s", name); SNPRINTF_FAILURE(size, len, offset); if (ipset_data_test(data, IPSET_OPT_NAMEREF)) { bool before = false; if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FLAGS))) { const uint32_t *flags = ipset_data_get(data, IPSET_OPT_FLAGS); before = (*flags) & IPSET_FLAG_BEFORE; } size = snprintf(buf + offset, len, " %s %s", before ? "before" : "after", (const char *) ipset_data_get(data, IPSET_OPT_NAMEREF)); SNPRINTF_FAILURE(size, len, offset); } return offset; }
IPSET_ARG_NONE, }, .need = 0, .full = 0, .help = "", }, [IPSET_ADD] = { .args = { IPSET_ARG_TIMEOUT, IPSET_ARG_NOMATCH, IPSET_ARG_PACKETS, IPSET_ARG_BYTES, IPSET_ARG_ADT_COMMENT, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_IP2), .full = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_CIDR) | IPSET_FLAG(IPSET_OPT_IP_TO) | IPSET_FLAG(IPSET_OPT_IP2) | IPSET_FLAG(IPSET_OPT_CIDR2) | IPSET_FLAG(IPSET_OPT_IP2_TO), .help = "IP[/CIDR]|FROM-TO,IP[/CIDR]|FROM-TO", }, [IPSET_DEL] = { .args = { IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_IP2),
/** * ipset_parse_proto_port - parse (optional) protocol and a single port * @session: session structure * @opt: option kind of the data * @str: string to parse * * Parse string as a protocol and port, separated by a colon. * The protocol part is optional. * The parsed protocol and port numbers are stored in the data * blob of the session. * * Returns 0 on success or a negative error code. */ int ipset_parse_proto_port(struct ipset_session *session, enum ipset_opt opt, const char *str) { struct ipset_data *data; char *a, *saved, *tmp; const char *proto; uint8_t p = IPPROTO_TCP; int err = 0; assert(session); assert(opt == IPSET_OPT_PORT); assert(str); data = ipset_session_data(session); saved = tmp = strdup(str); if (tmp == NULL) return ipset_err(session, "Cannot allocate memory to duplicate %s.", str); a = proto_separator(tmp); if (a != NULL) { uint8_t family = ipset_data_family(data); /* proto:port */ *a++ = '\0'; err = ipset_parse_proto(session, IPSET_OPT_PROTO, tmp); if (err) goto error; p = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); switch (p) { case IPPROTO_TCP: proto = tmp; tmp = a; goto parse_port; case IPPROTO_UDP: proto = tmp; tmp = a; goto parse_port; case IPPROTO_ICMP: if (family != AF_INET) { syntax_err("Protocol ICMP can be used with family INET only"); goto error; } err = ipset_parse_icmp(session, opt, a); break; case IPPROTO_ICMPV6: if (family != AF_INET6) { syntax_err("Protocol ICMPv6 can be used with family INET6 only"); goto error; } err = ipset_parse_icmpv6(session, opt, a); break; default: if (!STREQ(a, "0")) { syntax_err("Protocol %s can be used with pseudo port value 0 only."); goto error; } ipset_data_flags_set(data, IPSET_FLAG(opt)); } goto error; } else { proto = "TCP"; err = ipset_data_set(data, IPSET_OPT_PROTO, &p); if (err) goto error; } parse_port: err = ipset_parse_tcpudp_port(session, opt, tmp, proto); error: free(saved); return err; }
.opt = IPSET_OPT_IP }, }, .cmd = { [IPSET_CREATE] = { .args = { IPSET_ARG_IPRANGE, IPSET_ARG_NETMASK, IPSET_ARG_TIMEOUT, /* Backward compatibility */ IPSET_ARG_FROM_IP, IPSET_ARG_TO_IP, IPSET_ARG_NETWORK, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_IP_TO), .full = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_IP_TO), .help = "range IP/CIDR|FROM-TO", }, [IPSET_ADD] = { .args = { IPSET_ARG_TIMEOUT, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_IP), .full = IPSET_FLAG(IPSET_OPT_IP) | IPSET_FLAG(IPSET_OPT_IP_TO), .help = "IP|IP/CIDR|FROM-TO", },
.parse = ipset_parse_tcp_udp_port, .print = ipset_print_port, .opt = IPSET_OPT_PORT }, }, .cmd = { [IPSET_CREATE] = { .args = { IPSET_ARG_PORTRANGE, IPSET_ARG_TIMEOUT, /* Backward compatibility */ IPSET_ARG_FROM_PORT, IPSET_ARG_TO_PORT, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT_TO), .full = IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT_TO), .help = "range [PROTO:]FROM-TO", }, [IPSET_ADD] = { .args = { IPSET_ARG_TIMEOUT, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_PORT), .full = IPSET_FLAG(IPSET_OPT_PORT) | IPSET_FLAG(IPSET_OPT_PORT_TO), .help = "[PROTO:]PORT|FROM-TO", },
IPSET_ARG_SIZE, IPSET_ARG_TIMEOUT, IPSET_ARG_NONE, }, .need = 0, .full = 0, .help = "", }, [IPSET_ADD] = { .args = { IPSET_ARG_TIMEOUT, IPSET_ARG_BEFORE, IPSET_ARG_AFTER, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_NAME), .full = IPSET_FLAG(IPSET_OPT_NAME) | IPSET_FLAG(IPSET_OPT_BEFORE), .help = "NAME [before|after NAME]", }, [IPSET_DEL] = { .args = { IPSET_ARG_BEFORE, IPSET_ARG_AFTER, IPSET_ARG_NONE, }, .need = IPSET_FLAG(IPSET_OPT_NAME), .full = IPSET_FLAG(IPSET_OPT_NAME) | IPSET_FLAG(IPSET_OPT_BEFORE), .help = "NAME [before|after NAME]", },