/** * ipset_print_ipaddr - print IPv4|IPv6 address to string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print IPv4|IPv6 address or address/cidr to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_ipaddr(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env) { const union nf_inet_addr *ip; uint8_t family, cidr; enum ipset_opt cidropt; int flags; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP_TO || opt == IPSET_OPT_IP2); family = ipset_data_family(data); cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2; if (ipset_data_test(data, cidropt)) cidr = *(const uint8_t *) ipset_data_get(data, cidropt); else cidr = family == AF_INET6 ? 128 : 32; flags = (env & IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST; ip = ipset_data_get(data, opt); assert(ip); if (family == AF_INET) return snprintf_ipv4(buf, len, flags, ip, cidr); else if (family == AF_INET6) return snprintf_ipv6(buf, len, flags, ip, cidr); return -1; }
/** * ipset_print_port - print port or port range * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print port or port range to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_port(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt ASSERT_UNUSED, uint8_t env UNUSED) { const uint16_t *port; int size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_PORT); if (len < 2*strlen("65535") + 2) return -1; port = ipset_data_get(data, IPSET_OPT_PORT); assert(port); size = snprintf(buf, len, "%u", *port); SNPRINTF_FAILURE(size, len, offset); if (ipset_data_test(data, IPSET_OPT_PORT_TO)) { port = ipset_data_get(data, IPSET_OPT_PORT_TO); size = snprintf(buf + offset, len, "%s%u", IPSET_RANGE_SEPARATOR, *port); SNPRINTF_FAILURE(size, len, offset); } return offset; }
/** * ipset_print_ip - print IPv4|IPv6 address to string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print IPv4|IPv6 address, address/cidr or address range to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_ip(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env) { const union nf_inet_addr *ip; uint8_t family, cidr; int flags, size, offset = 0; enum ipset_opt cidropt; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2); D("len: %u", len); family = ipset_data_family(data); cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2; if (ipset_data_test(data, cidropt)) { cidr = *(const uint8_t *) ipset_data_get(data, cidropt); D("CIDR: %u", cidr); } else cidr = family == AF_INET6 ? 128 : 32; flags = (env & IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST; ip = ipset_data_get(data, opt); assert(ip); if (family == AF_INET) size = snprintf_ipv4(buf, len, flags, ip, cidr); else if (family == AF_INET6) size = snprintf_ipv6(buf, len, flags, ip, cidr); else return -1; D("size %i, len %u", size, len); SNPRINTF_FAILURE(size, len, offset); D("len: %u, offset %u", len, offset); if (!ipset_data_test(data, IPSET_OPT_IP_TO)) return offset; size = snprintf(buf + offset, len, "%s", IPSET_RANGE_SEPARATOR); SNPRINTF_FAILURE(size, len, offset); ip = ipset_data_get(data, IPSET_OPT_IP_TO); if (family == AF_INET) size = snprintf_ipv4(buf + offset, len, flags, ip, cidr); else if (family == AF_INET6) size = snprintf_ipv6(buf + offset, len, flags, ip, cidr); else return -1; SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_print_number - print number to string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print number to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_number(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { size_t maxsize; const void *number; assert(buf); assert(len > 0); assert(data); number = ipset_data_get(data, opt); maxsize = ipset_data_sizeof(opt, AF_INET); D("opt: %u, maxsize %zu", opt, maxsize); if (maxsize == sizeof(uint8_t)) return snprintf(buf, len, "%u", *(const uint8_t *) number); else if (maxsize == sizeof(uint16_t)) return snprintf(buf, len, "%u", *(const uint16_t *) number); else if (maxsize == sizeof(uint32_t)) return snprintf(buf, len, "%lu", (long unsigned) *(const uint32_t *) number); else assert(0); return 0; }
/** * ipset_print_iface - print interface element string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print interface element string to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_iface(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_IFACE); if (len < IFNAMSIZ + strlen("physdev:")) return -1; if (ipset_data_test(data, IPSET_OPT_PHYSDEV)) { size = snprintf(buf, len, "physdev:"); SNPRINTF_FAILURE(size, len, offset); } name = ipset_data_get(data, opt); assert(name); size = snprintf(buf, len, "%s", name); SNPRINTF_FAILURE(size, len, offset); return offset; }
int ipset_print_skbmark(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { int size, offset = 0; const uint64_t *skbmark; uint32_t mark, mask; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_SKBMARK); skbmark = ipset_data_get(data, IPSET_OPT_SKBMARK); assert(skbmark); mark = *skbmark >> 32; mask = *skbmark & 0xffffffff; if (mask == 0xffffffff) size = snprintf(buf + offset, len, "0x%"PRIx32, mark); else size = snprintf(buf + offset, len, "0x%"PRIx32"/0x%"PRIx32, mark, mask); SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_print_ether - print ethernet address to string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print Ethernet address to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_ether(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { const unsigned char *ether; int i, size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_ETHER); if (len < ETH_ALEN*3) return -1; ether = ipset_data_get(data, opt); assert(ether); size = snprintf(buf, len, "%02X", ether[0]); SNPRINTF_FAILURE(size, len, offset); for (i = 1; i < ETH_ALEN; i++) { size = snprintf(buf + offset, len, ":%02X", ether[i]); SNPRINTF_FAILURE(size, len, offset); } return offset; }
/** * ipset_print_elem - print ADT elem according to settype * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print (multipart) element according to settype * * Return lenght of printed string or error size. */ int ipset_print_elem(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt UNUSED, uint8_t env) { const struct ipset_type *type; int size, offset = 0; assert(buf); assert(len > 0); assert(data); type = ipset_data_get(data, IPSET_OPT_TYPE); if (!type) return -1; size = type->elem[IPSET_DIM_ONE - 1].print(buf, len, data, type->elem[IPSET_DIM_ONE - 1].opt, env); SNPRINTF_FAILURE(size, len, offset); IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO - 1].opt), "print second elem"); if (type->dimension == IPSET_DIM_ONE || (type->last_elem_optional && !ipset_data_test(data, type->elem[IPSET_DIM_TWO - 1].opt))) return offset; size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR); SNPRINTF_FAILURE(size, len, offset); size = type->elem[IPSET_DIM_TWO - 1].print(buf + offset, len, data, type->elem[IPSET_DIM_TWO - 1].opt, env); SNPRINTF_FAILURE(size, len, offset); if (type->dimension == IPSET_DIM_TWO || (type->last_elem_optional && !ipset_data_test(data, type->elem[IPSET_DIM_THREE - 1].opt))) return offset; size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR); SNPRINTF_FAILURE(size, len, offset); size = type->elem[IPSET_DIM_THREE - 1].print(buf + offset, len, data, type->elem[IPSET_DIM_THREE - 1].opt, env); SNPRINTF_FAILURE(size, len, offset); if (type->dimension == IPSET_DIM_THREE || (type->last_elem_optional && !ipset_data_test(data, type->elem[IPSET_DIM_FOUR - 1].opt))) return offset; size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR); SNPRINTF_FAILURE(size, len, offset); size = type->elem[IPSET_DIM_FOUR - 1].print(buf + offset, len, data, type->elem[IPSET_DIM_FOUR - 1].opt, env); SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * 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_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_print_comment - print arbitrary parameter string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print arbitrary string to output buffer. * * Return length of printed string or error size. */ int ipset_print_comment(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { const char *comment; int size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_ADT_COMMENT); comment = ipset_data_get(data, opt); assert(comment); size = snprintf(buf + offset, len, "\"%s\"", comment); SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_print_type - print ipset type string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print ipset module string identifier to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_type(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { const struct ipset_type *type; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_TYPE); type = ipset_data_get(data, opt); assert(type); if (len < strlen(type->name) + 1) return -1; return snprintf(buf, len, "%s", type->name); }
int ipset_print_skbprio(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt, uint8_t env UNUSED) { int size, offset = 0; const uint32_t *skbprio; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_SKBPRIO); skbprio = ipset_data_get(data, opt); assert(skbprio); size = snprintf(buf + offset, len, "%x:%x", *skbprio >> 16, *skbprio & 0xffff); SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_print_icmpv6 - print ICMPv6 code name or type/code * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print ICMPv6 code name or type/code name to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_icmpv6(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt ASSERT_UNUSED, uint8_t env UNUSED) { const char *name; uint16_t typecode; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_PORT); typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT); name = icmpv6_to_name(typecode >> 8, typecode & 0xFF); if (name != NULL) return snprintf(buf, len, "%s", name); else return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF); }
/** * ipset_print_mark - print mark to string * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print mark to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_mark(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt ASSERT_UNUSED, uint8_t env UNUSED) { const uint32_t *mark; int size, offset = 0; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_MARK || opt == IPSET_OPT_MARKMASK); mark = ipset_data_get(data, opt); assert(mark); size = snprintf(buf, len, "0x%08"PRIx32, *mark); SNPRINTF_FAILURE(size, len, offset); return offset; }
/** * ipset_print_proto - print protocol name * @buf: printing buffer * @len: length of available buffer space * @data: data blob * @opt: the option kind * @env: environment flags * * Print protocol name to output buffer. * * Return lenght of printed string or error size. */ int ipset_print_proto(char *buf, unsigned int len, const struct ipset_data *data, enum ipset_opt opt ASSERT_UNUSED, uint8_t env UNUSED) { const struct protoent *protoent; uint8_t proto; assert(buf); assert(len > 0); assert(data); assert(opt == IPSET_OPT_PROTO); proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO); assert(proto); protoent = getprotobynumber(proto); if (protoent) return snprintf(buf, len, "%s", protoent->p_name); /* Should not happen */ return snprintf(buf, len, "%u", proto); }
/** * 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; }