Пример #1
0
static void
route_print(FILE *file, void *data)
{
	ip_route_t *route = data;

	fprintf(file, "     ");

	if (route->blackhole)
		fprintf(file, "blackhole ");
	if (route->dst)
		fprintf(file, "%s/%d", ipaddresstos(NULL, route->dst), route->dmask);
	if (route->gw)
		fprintf(file, " gw %s", ipaddresstos(NULL, route->gw));
	if (route->gw2)
		fprintf(file, " or gw %s", ipaddresstos(NULL, route->gw2));
	if (route->src)
		fprintf(file, " src %s", ipaddresstos(NULL, route->src));
	if (route->index)
		fprintf(file, " dev %s", IF_NAME(if_get_by_ifindex(route->index)));
	if (route->table)
		fprintf(file, " table %d", route->table);
	if (route->scope)
		fprintf(file, " scope %s", netlink_scope_n2a(route->scope));
	if (route->metric)
		fprintf(file, " metric %d", route->metric);

	fprintf(file, "\n");
}
Пример #2
0
void
dump_iproute(void *rt_data)
{
	ip_route_t *route = rt_data;
	char *log_msg = MALLOC(1024);
	char *op = log_msg;

	if (route->blackhole) {
		strncat(log_msg, "blackhole ", 30);
	}
	if (route->dst)
		op += snprintf(op, log_msg + 1024 - op, "%s/%d", ipaddresstos(NULL, route->dst), route->dmask);
	if (route->gw)
		op += snprintf(op, log_msg + 1024 - op, " gw %s", ipaddresstos(NULL, route->gw));
	if (route->gw2)
		op += snprintf(op, log_msg + 1024 - op, " or gw %s", ipaddresstos(NULL, route->gw2));
	if (route->src)
		op += snprintf(op, log_msg + 1024 - op, " src %s", ipaddresstos(NULL, route->src));
	if (route->index)
		op += snprintf(op, log_msg + 1024 - op, " dev %s",
			 IF_NAME(if_get_by_ifindex(route->index)));
	if (route->table)
		op += snprintf(op, log_msg + 1024 - op, " table %d", route->table);
	if (route->scope)
		op += snprintf(op, log_msg + 1024 - op, " scope %s",
			 netlink_scope_n2a(route->scope));
	if (route->metric)
		op += snprintf(op, log_msg + 1024 - op, " metric %d", route->metric);

	log_message(LOG_INFO, "     %s", log_msg);

	FREE(log_msg);
}
Пример #3
0
void
dump_iproute(void *rt_data)
{
	ip_route_t *route = rt_data;
	char *log_msg = MALLOC(1024);
	char *tmp = MALLOC(INET6_ADDRSTRLEN + 30);
	char *tmp_str;

	if (route->blackhole) {
		strncat(log_msg, "blackhole ", 30);
	}
	if (route->dst) {
		tmp_str = ipaddresstos(route->dst);
		snprintf(tmp, INET6_ADDRSTRLEN + 30, "%s/%d", tmp_str, route->dmask);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
		FREE(tmp_str);
	}
	if (route->gw) {
		tmp_str = ipaddresstos(route->gw);
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " gw %s", tmp_str);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
		FREE(tmp_str);
	}
	if (route->gw2) {
		tmp_str = ipaddresstos(route->gw2);
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " or gw %s", tmp_str);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
		FREE(tmp_str);
	}
	if (route->src) {
		tmp_str = ipaddresstos(route->src);
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " src %s", tmp_str);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
		FREE(tmp_str);
	}
	if (route->index) {
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " dev %s",
			 IF_NAME(if_get_by_ifindex(route->index)));
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
	}
	if (route->table) {
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " table %d", route->table);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
	}
	if (route->scope) {
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " scope %s",
			 netlink_scope_n2a(route->scope));
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
	}
	if (route->metric) {
		snprintf(tmp, INET6_ADDRSTRLEN + 30, " metric %d", route->metric);
		strncat(log_msg, tmp, INET6_ADDRSTRLEN + 30);
	}

	log_message(LOG_INFO, "     %s", log_msg);

	FREE(tmp);
	FREE(log_msg);
}
Пример #4
0
void
route_print(FILE *file, void *data)
{
	ip_route_t *route = data;
	char *msg = MALLOC(150);
	char *tmp = MALLOC(30);

	if (route->blackhole) {
		strncat(msg, "blackhole ", 30);
	}
	if (route->dst) {
		snprintf(tmp, 30, "%s/%d", ipaddresstos(route->dst),
			route->dmask);
		strncat(msg, tmp, 30);
	}
	if (route->gw) {
		snprintf(tmp, 30, " gw %s", ipaddresstos(route->gw));
		strncat(msg, tmp, 30);
	}
	if (route->gw2) {
		snprintf(tmp, 30, " or gw %s", ipaddresstos(route->gw2));
		strncat(msg, tmp, 30);
	}
	if (route->src) {
		snprintf(tmp, 30, " src %s", ipaddresstos(route->src));
		strncat(msg, tmp, 30);
	}
	if (route->index) {
		snprintf(tmp, 30, " dev %s",
		  IF_NAME(if_get_by_ifindex(route->index)));
		strncat(msg, tmp, 30);
	}
	if (route->table) {
		snprintf(tmp, 30, " table %d", route->table);
		strncat(msg, tmp, 30);
	}
	if (route->scope) {
		snprintf(tmp, 30, " scope %s",
		  netlink_scope_n2a(route->scope));
		strncat(msg, tmp, 30);
	}
	if (route->metric) {
		snprintf(tmp, 30, " metric %d", route->metric);
		strncat(msg, tmp, 30);
	}

	fprintf(file, "     %s\n", msg);

	FREE(tmp);
	FREE(msg);

}
Пример #5
0
void
rule_print(FILE *file, void *data)
{
	ip_rule_t *rule = data;
	char *msg = MALLOC(150);
	char *tmp = MALLOC(30);

	if (rule->dir) {
		snprintf(tmp, 30, "%s ", rule->dir);
		strncat(msg, tmp, 30);
	}
	if (rule->addr) {
		snprintf(tmp, 30, "%s", ipaddresstos(rule->addr));
		strncat(msg, tmp, 30);
	}
	if (rule->table) {
		snprintf(tmp, 30, " table %d", rule->table);
		strncat(msg, tmp, 30);
	}

	fprintf(file, "     %s\n", msg);

	FREE(tmp);
	FREE(msg);

}
Пример #6
0
static size_t
print_encap_ip6(char *op, size_t len, const encap_t* encap)
{
	char *opn = op;

	opn += snprintf(opn, len - (opn - op), " encap ip6");

	if (encap->flags & IPROUTE_BIT_ENCAP_ID)
		opn += snprintf(opn, len - (opn - op), " id %" PRIu64, encap->ip6.id);
	if (encap->ip.dst)
		opn += snprintf(opn, len - (opn - op), " dst %s", ipaddresstos(NULL, encap->ip6.dst));
	if (encap->ip.src)
		opn += snprintf(opn, len - (opn - op), " src %s", ipaddresstos(NULL, encap->ip6.src));
	if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD)
		opn += snprintf(opn, len - (opn - op), " tc %d", encap->ip6.tc);
	if (encap->flags & IPROUTE_BIT_ENCAP_HOPLIMIT)
		opn += snprintf(opn, len - (opn - op), " hoplimit %d", encap->ip6.hoplimit);
	if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS)
		opn += snprintf(opn, len - (opn - op), " flags 0x%x", encap->ip6.flags);

	return opn - op;
}
Пример #7
0
static size_t
print_encap_ip(char *op, size_t len, const encap_t* encap)
{
	char *opn = op;

	opn += snprintf(opn, len - (opn - op), " encap ip");

	if (encap->flags & IPROUTE_BIT_ENCAP_ID)
		opn += snprintf(opn, len - (opn - op), " id %" PRIu64, encap->ip.id);
	if (encap->ip.dst)
		opn += snprintf(opn, len - (opn - op), " dst %s", ipaddresstos(NULL, encap->ip.dst));
	if (encap->ip.src)
		opn += snprintf(opn, len - (opn - op), " src %s", ipaddresstos(NULL, encap->ip.src));
	if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD)
		opn += snprintf(opn, len - (opn - op), " tos %d", encap->ip.tos);
	if (encap->flags & IPROUTE_BIT_ENCAP_TTL)
		opn += snprintf(opn, len - (opn - op), " ttl %d", encap->ip.ttl);
	if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS)
		opn += snprintf(opn, len - (opn - op), " flags 0x%x", encap->ip.flags);

	return opn - op;
}
Пример #8
0
/* add/remove iptable drop rule to VIP */
static void
handle_iptable_rule_to_vip(ip_address_t *ipaddress, int cmd, char *ifname, void *unused)
{
	char  *argv[10];
	unsigned int i = 0;
	int if_specifier = -1;
	char *addr_str;

	if (global_data->vrrp_iptables_inchain[0] == '\0')
		return;

	if (IP_IS6(ipaddress)) {
		handle_iptable_rule_to_NA(ipaddress, cmd, ifname);
		argv[i++] = "ip6tables";
	} else {
		argv[i++] = "iptables";
	}

	addr_str = ipaddresstos(NULL, ipaddress);

	argv[i++] = cmd ? "-A" : "-D";
	argv[i++] = global_data->vrrp_iptables_inchain;
	argv[i++] = "-d";
	argv[i++] = addr_str;
	if (IP_IS6(ipaddress) && IN6_IS_ADDR_LINKLOCAL(&ipaddress->u.sin6_addr)) {
		if_specifier = i;
		argv[i++] = "-i";
		argv[i++] = ifname;
	}
	argv[i++] = "-j";
	argv[i++] = "DROP";
	argv[i] = NULL;

	if (fork_exec(argv) < 0)
		log_message(LOG_ERR, "Failed to %s iptable drop rule"
				     " to vip %s", (cmd) ? "set" : "remove", addr_str);
	else
		ipaddress->iptable_rule_set = (cmd != IPADDRESS_DEL);

	if (global_data->vrrp_iptables_outchain[0] == '\0')
		return;

	argv[2] = global_data->vrrp_iptables_outchain ;
	argv[3] = "-s";
	if (if_specifier >= 0)
		argv[if_specifier] = "-o";

	if (fork_exec(argv) < 0)
		log_message(LOG_ERR, "Failed to %s iptable drop rule"
				     " from vip %s", (cmd) ? "set" : "remove", addr_str);
}
Пример #9
0
static void
rule_print(FILE *file, void *data)
{
	ip_rule_t *rule = data;

	fprintf(file, "     ");

	if (rule->dir)
		fprintf(file, "%s ", (rule->dir == VRRP_RULE_FROM) ? "from" : "to");
	if (rule->addr)
		fprintf(file, "%s", ipaddresstos(NULL, rule->addr));
	if (rule->table)
		fprintf(file, " table %d", rule->table);

	fprintf(file, "\n");
}
Пример #10
0
void
dump_iprule(void *rule_data)
{
	ip_rule_t *rule = rule_data;
	char *log_msg = MALLOC(1024);
	char *op = log_msg;

	if (rule->dir)
		op += snprintf(op, log_msg + 1024 - op, "%s ", (rule->dir == VRRP_RULE_FROM) ? "from" : "to");
	if (rule->addr)
		op += snprintf(op, log_msg + 1024 - op, "%s/%d", ipaddresstos(NULL, rule->addr), rule->mask);
	if (rule->table)
		op += snprintf(op, log_msg + 1024 - op, " table %d", rule->table);

	log_message(LOG_INFO, "     %s", log_msg);

	FREE(log_msg);
}
Пример #11
0
void
dump_ipaddress(void *if_data)
{
	ip_address_t *ipaddr = if_data;
	char broadcast[INET_ADDRSTRLEN + 5] = "";

	if (!IP_IS6(ipaddr) && ipaddr->u.sin.sin_brd.s_addr) {
		snprintf(broadcast, 21, " brd %s",
			 inet_ntop2(ipaddr->u.sin.sin_brd.s_addr));
	}

	log_message(LOG_INFO, "     %s/%d%s dev %s scope %s%s%s"
			    , ipaddresstos(NULL, ipaddr)
			    , ipaddr->ifa.ifa_prefixlen
			    , broadcast
			    , IF_NAME(ipaddr->ifp)
			    , get_rttables_scope(ipaddr->ifa.ifa_scope)
			    , ipaddr->label ? " label " : ""
			    , ipaddr->label ? ipaddr->label : "");
}
Пример #12
0
/* Add/Delete IP address to a specific interface_t */
int
netlink_ipaddress(ip_address_t *ipaddress, int cmd)
{
	struct ifa_cacheinfo cinfo;
	int status = 1;
	struct {
		struct nlmsghdr n;
		struct ifaddrmsg ifa;
		char buf[256];
	} req;

	memset(&req, 0, sizeof (req));

	req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifaddrmsg));
	req.n.nlmsg_flags = NLM_F_REQUEST;
	req.n.nlmsg_type = (cmd == IPADDRESS_DEL) ? RTM_DELADDR : RTM_NEWADDR;
	req.ifa = ipaddress->ifa;

	if (IP_IS6(ipaddress)) {
		if (cmd == IPADDRESS_ADD) {
			/* Mark IPv6 address as deprecated (rfc3484) in order to prevent
			 * using VRRP VIP as source address in healthchecking use cases.
			 */
			if (ipaddress->ifa.ifa_prefixlen == 128) {
				memset(&cinfo, 0, sizeof(cinfo));
				cinfo.ifa_prefered = 0;
				cinfo.ifa_valid = INFINITY_LIFE_TIME;

				log_message(LOG_INFO, "%s has a prefix length of 128, setting "
						      "preferred_lft to 0", ipaddresstos(NULL, ipaddress));
				addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo,
					  sizeof(cinfo));
			}

			/* Disable, per VIP, Duplicate Address Detection algorithm (DAD).
			 * Using the nodad flag has the following benefits:
			 *
			 * (1) The address becomes immediately usable after they're
			 *     configured.
			 * (2) In the case of a temporary layer-2 / split-brain problem
			 *     we can avoid that the active VIP transitions into the
			 *     dadfailed phase and stays there forever - leaving us
			 *     without service. HA/VRRP setups have their own "DAD"-like
			 *     functionality, so it's not really needed from the IPv6 stack.
			 */
#ifdef IFA_F_NODAD	/* Since Linux 2.6.19 */
			req.ifa.ifa_flags |= IFA_F_NODAD;
#endif
		}

		addattr_l(&req.n, sizeof(req), IFA_LOCAL,
			  &ipaddress->u.sin6_addr, sizeof(ipaddress->u.sin6_addr));
	} else {
		addattr_l(&req.n, sizeof(req), IFA_LOCAL,
			  &ipaddress->u.sin.sin_addr, sizeof(ipaddress->u.sin.sin_addr));

		if (cmd == IPADDRESS_ADD) {
			if (ipaddress->u.sin.sin_brd.s_addr)
				addattr_l(&req.n, sizeof(req), IFA_BROADCAST,
					  &ipaddress->u.sin.sin_brd, sizeof(ipaddress->u.sin.sin_brd));
		}
		else {
			/* IPADDRESS_DEL */
			addattr_l(&req.n, sizeof(req), IFA_ADDRESS,
				  &ipaddress->u.sin.sin_addr, sizeof(ipaddress->u.sin.sin_addr));
		}
	}

	if (cmd == IPADDRESS_ADD)
		if (ipaddress->label)
			addattr_l(&req.n, sizeof (req), IFA_LABEL,
				  ipaddress->label, strlen(ipaddress->label) + 1);

	if (netlink_talk(&nl_cmd, &req.n) < 0)
		status = -1;

	return status;
}
Пример #13
0
static void
handle_iptable_rule_to_NA(ip_address_t *ipaddress, int cmd, char *ifname, bool force)
{
	char  *argv[14];
	unsigned int i = 0;
	int if_specifier = -1;
	int type_specifier ;
	char *addr_str;

	if (global_data->vrrp_iptables_inchain[0] == '\0')
		return;

	addr_str = ipaddresstos(NULL, ipaddress);

	argv[i++] = "ip6tables";
	argv[i++] = cmd ? "-A" : "-D";
	argv[i++] = global_data->vrrp_iptables_inchain;
	argv[i++] = "-d";
	argv[i++] = addr_str;
	if (IN6_IS_ADDR_LINKLOCAL(&ipaddress->u.sin6_addr)) {
		if_specifier = i;
		argv[i++] = "-i";
		argv[i++] = ifname;
	}
	argv[i++] = "-p";
	argv[i++] = "icmpv6";
	argv[i++] = "--icmpv6-type";
	type_specifier = i;
	argv[i++] = "136";
	argv[i++] = "-j";
	argv[i++] = "ACCEPT";
	argv[i] = NULL;

	if (fork_exec(argv) < 0 && !force)
		log_message(LOG_ERR, "Failed to %s ip6table rule to accept NAs sent"
				     " to vip %s", (cmd) ? "set" : "remove", addr_str);

	argv[type_specifier] = "135";

	if (fork_exec(argv) < 0 && !force)
		log_message(LOG_ERR, "Failed to %s ip6table rule to accept NSs sent"
				     " to vip %s", (cmd) ? "set" : "remove", addr_str);

	if (global_data->vrrp_iptables_outchain[0] == '\0')
		return;

	argv[2] = global_data->vrrp_iptables_outchain;
	argv[3] = "-s";
	if (if_specifier >= 0)
		argv[if_specifier] = "-o";

	/* Allow NSs to be sent - this should only happen if the underlying interface
	   doesn't have an IPv6 address */
	if (fork_exec(argv) < 0 && !force)
		log_message(LOG_ERR, "Failed to %s ip6table rule to allow NSs to be"
				     " sent from vip %s", (cmd) ? "set" : "remove", addr_str);

	argv[type_specifier] = "136";

	/* Allow NAs to be sent in reply to an NS */
	if (fork_exec(argv) < 0 && !force)
		log_message(LOG_ERR, "Failed to %s ip6table rule to allow NAs to be"
				     " sent from vip %s", (cmd) ? "set" : "remove", addr_str);
}
Пример #14
0
void
format_iproute(ip_route_t *route, char *buf, size_t buf_len)
{
	char *op = buf;
	char *buf_end = buf + buf_len;
	nexthop_t *nh;
	element e;

	if (route->type != RTN_UNICAST)
		op += snprintf(op, buf_end - op, " %s", get_rttables_rtntype(route->type));
	if (route->dst) {
		op += snprintf(op, buf_end - op, " %s", ipaddresstos(NULL, route->dst));
		if ((route->dst->ifa.ifa_family == AF_INET && route->dst->ifa.ifa_prefixlen != 32 ) ||
		    (route->dst->ifa.ifa_family == AF_INET6 && route->dst->ifa.ifa_prefixlen != 128 ))
			op += snprintf(op, buf_end - op, "/%u", route->dst->ifa.ifa_prefixlen);
	}
	else
		op += snprintf(op, buf_end - op, " %s", "default");

	if (route->src) {
		op += snprintf(op, buf_end - op, " from %s", ipaddresstos(NULL, route->src));
		if ((route->src->ifa.ifa_family == AF_INET && route->src->ifa.ifa_prefixlen != 32 ) ||
		    (route->src->ifa.ifa_family == AF_INET6 && route->src->ifa.ifa_prefixlen != 128 ))
			op += snprintf(op, buf_end - op, "/%u", route->src->ifa.ifa_prefixlen);
	}

//#ifdef _HAVE_RTA_NEWDST_
//	/* MPLS only */
//	if (route->as_to)
//		op += snprintf(op, buf_end - op, " as to %s", ipaddresstos(NULL, route->as_to));
//#endif

	if (route->pref_src)
		op += snprintf(op, buf_end - op, " src %s", ipaddresstos(NULL, route->pref_src));

	if (route->mask & IPROUTE_BIT_DSFIELD)
		op += snprintf(op, buf_end - op, " tos %u", route->tos);

#ifdef _HAVE_RTA_ENCAP_
	if (route->encap.type != LWTUNNEL_ENCAP_NONE)
		op += print_encap(op, buf_end - op, &route->encap);
#endif

	if (route->via)
		op += snprintf(op, buf_end - op, " via %s %s", route->via->ifa.ifa_family == AF_INET6 ? "inet6" : "inet", ipaddresstos(NULL, route->via));

	if (route->oif)
		op += snprintf(op, buf_end - op, " dev %s", route->oif->ifname);

	if (route->table != RT_TABLE_MAIN)
		op += snprintf(op, buf_end - op, " table %u", route->table);

	if (route->mask & IPROUTE_BIT_PROTOCOL)
		op += snprintf(op, buf_end - op, " proto %u", route->protocol);

	if (route->mask & IPROUTE_BIT_SCOPE)
		op += snprintf(op, buf_end - op, " scope %u", route->scope);

	if (route->mask & IPROUTE_BIT_METRIC)
		op += snprintf(op, buf_end - op, " metric %u", route->metric);

	if (route->family == AF_INET && route->flags & RTNH_F_ONLINK)
		op += snprintf(op, buf_end - op, " %s", "onlink");

	if (route->realms) {
		if (route->realms & 0xFFFF0000)
			op += snprintf(op, buf_end - op, " realms %d/", route->realms >> 16);
		else
			op += snprintf(op, buf_end - op, " realm ");
		op += snprintf(op, buf_end - op, "%d", route->realms & 0xFFFF);
	}
Пример #15
0
void
format_iprule(ip_rule_t *rule, char *buf, size_t buf_len)
{
	char *op = buf;
	char *buf_end = buf + buf_len;

	if (!rule->to_addr && !rule->from_addr && rule->family == AF_INET6)
		op += snprintf(op, (size_t)(buf_end - op), "inet6 ");

	if (rule->invert)
		op += snprintf(op, (size_t)(buf_end - op), "not ");

	if (rule->from_addr)
		op += snprintf(op, (size_t)(buf_end - op), "from %s", ipaddresstos(NULL, rule->from_addr));
	else
		op += snprintf(op, (size_t)(buf_end - op), "from all" );

	if (rule->to_addr)
		op += snprintf(op, (size_t)(buf_end - op), " to %s", ipaddresstos(NULL, rule->to_addr));

	if (rule->mask & IPRULE_BIT_PRIORITY)
		op += snprintf(op, (size_t)(buf_end - op), " priority %u", rule->priority);

	op += snprintf(op, (size_t)(buf_end - op), " tos 0x%x", rule->tos);

	if (rule->mask & (IPRULE_BIT_FWMARK | IPRULE_BIT_FWMASK)) {
		op += snprintf(op, (size_t)(buf_end - op), " fwmark 0x%x", rule->fwmark);

		if (rule->mask & IPRULE_BIT_FWMASK && rule->fwmask != 0xffffffff)
			op += snprintf(op, (size_t)(buf_end - op), "/0x%x", rule->fwmask);
	}

	if (rule->iif)
#if HAVE_DECL_FRA_OIFNAME
		op += snprintf(op, (size_t)(buf_end - op), " iif %s", rule->iif->ifname);
#else
		op += snprintf(op, (size_t)(buf_end - op), " dev %s", rule->iif->ifname);
#endif

#if HAVE_DECL_FRA_OIFNAME
	if (rule->oif)
		op += snprintf(op, (size_t)(buf_end - op), " oif %s", rule->oif->ifname);
#endif

#if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN
	if (rule->suppress_prefix_len != -1)
		op += snprintf(op, (size_t)(buf_end - op), " suppress_prefixlen %u", rule->suppress_prefix_len);
#endif

#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
	if (rule->mask & IPRULE_BIT_SUP_GROUP)
		op += snprintf(op, (size_t)(buf_end - op), " suppress_ifgroup %d", rule->suppress_group);
#endif

#if HAVE_DECL_FRA_TUN_ID
	if (rule->tunnel_id)
		op += snprintf(op, (size_t)(buf_end - op), " tunnel-id %" PRIu64, rule->tunnel_id);
#endif

#if HAVE_DECL_FRA_UID_RANGE
	if (rule->mask & IPRULE_BIT_UID_RANGE)
		op += snprintf(op, (size_t)(buf_end - op), " uidrange %" PRIu32 "-%" PRIu32, rule->uid_range.start, rule->uid_range.end);
#endif

#if HAVE_DECL_FRA_L3MDEV
	if (rule->l3mdev)
		op += snprintf(op, (size_t)(buf_end - op), " l3mdev");
#endif

#if HAVE_DECL_FRA_PROTOCOL
	if (rule->mask & IPRULE_BIT_PROTOCOL)
		op += snprintf(op, (size_t)(buf_end - op), " protocol %u", rule->protocol);
#endif

#if HAVE_DECL_FRA_IP_PROTO
	if (rule->mask & IPRULE_BIT_IP_PROTO)
		op += snprintf(op, (size_t)(buf_end - op), " ipproto %u", rule->ip_proto);
#endif

#if HAVE_DECL_FRA_SPORT_RANGE
	if (rule->mask & IPRULE_BIT_SPORT_RANGE)
		op += snprintf(op, (size_t)(buf_end - op), " sport %hu-%hu", rule->src_port.start, rule->src_port.end);
#endif

#if HAVE_DECL_FRA_DPORT_RANGE
	if (rule->mask & IPRULE_BIT_DPORT_RANGE)
		op += snprintf(op, (size_t)(buf_end - op), " dport %hu-%hu", rule->dst_port.start, rule->dst_port.end);
#endif

	if (rule->realms)
		op += snprintf(op, (size_t)(buf_end - op), " realms %d/%d", rule->realms >> 16, rule->realms & 0xffff);

	if (rule->action == FR_ACT_TO_TBL)
		op += snprintf(op, (size_t)(buf_end - op), " lookup %u", rule->table);
	else if (rule->action == FR_ACT_GOTO)
		op += snprintf(op, (size_t)(buf_end - op), " goto %u", rule->goto_target);
	else if (rule->action == FR_ACT_NOP)
		op += snprintf(op, (size_t)(buf_end - op), " nop");
	else
		op += snprintf(op, (size_t)(buf_end - op), " type %s", get_rttables_rtntype(rule->action));
	if (rule->dont_track)
		op += snprintf(op, (size_t)(buf_end - op), " no_track");
	if (rule->track_group)
		op += snprintf(op, (size_t)(buf_end - op), " track_group %s", rule->track_group->gname);
}