/* SPDX-License-Identifier: LGPL-2.1-only */ /* * Copyright (c) 2016 Magnus Öberg */ /** * @ingroup act * @defgroup act_nat NAT * * @{ */ #include #include #include #include #include #include #include #include static struct nla_policy nat_policy[TCA_NAT_MAX + 1] = { [TCA_NAT_PARMS] = { .minlen = sizeof(struct tc_nat) }, }; /** * nat operations */ static int nat_msg_parser(struct rtnl_tc *tc, void *data) { struct tc_nat *nat = data; struct nlattr *tb[TCA_NAT_MAX + 1]; int err; err = tca_parse(tb, TCA_NAT_MAX, tc, nat_policy); if (err < 0) return err; if (!tb[TCA_NAT_PARMS]) return -NLE_MISSING_ATTR; nla_memcpy(nat, tb[TCA_NAT_PARMS], sizeof(*nat)); return NLE_SUCCESS; } static void nat_free_data(struct rtnl_tc *tc, void *data) { } static int nat_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) { struct tc_nat *nat = data; if (!nat) return -NLE_OBJ_NOTFOUND; NLA_PUT(msg, TCA_NAT_PARMS, sizeof(*nat), nat); return NLE_SUCCESS; nla_put_failure: return -NLE_NOMEM; } static void nat_dump_line(struct rtnl_tc *tc, void *data, struct nl_dump_params *p) { struct tc_nat *nat = data; char buf[32]; uint32_t mask; int pfx = 0; if (!nat) return; if (nat->flags & TCA_NAT_FLAG_EGRESS) nl_dump(p, " egress"); else nl_dump(p, " ingress"); mask = nat->mask; while (mask > 0) { mask = mask >> 1; pfx++; } inet_ntop(AF_INET, &nat->old_addr, buf, sizeof(buf)); nl_dump(p, " %s", buf); if (pfx < 32) nl_dump(p, "/%d", pfx); inet_ntop(AF_INET, &nat->new_addr, buf, sizeof(buf)); nl_dump(p, " %s", buf); if (pfx < 32) nl_dump(p, "/%d", pfx); } /** * @name Attribute Modifications * @{ */ /** * Set old IPv4 address on a netlink NAT action object * @arg act Action object * @arg addr Binary IPv4 address in host byte order * * @return 0 on success or negative error code in case of an error. */ int rtnl_nat_set_old_addr(struct rtnl_act *act, in_addr_t addr) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) return -NLE_NOMEM; nat->old_addr = addr; return NLE_SUCCESS; } int rtnl_nat_get_old_addr(struct rtnl_act *act, in_addr_t *addr) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) return -NLE_NOATTR; *addr = nat->old_addr; return NLE_SUCCESS; } /** * Set new IPv4 address on a netlink NAT action object * @arg act Action object * @arg addr Binary IPv4 address in host byte order * * @return 0 on success or negative error code in case of an error. */ int rtnl_nat_set_new_addr(struct rtnl_act *act, in_addr_t addr) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) return -NLE_NOMEM; nat->new_addr = addr; return NLE_SUCCESS; } int rtnl_nat_get_new_addr(struct rtnl_act *act, in_addr_t *addr) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) return -NLE_NOATTR; *addr = nat->new_addr; return NLE_SUCCESS; } /** * Set IPv4 address mask on a netlink NAT action object * @arg act Action object * @arg mask IPv4 address mask * * @return 0 on success or negative error code in case of an error. */ int rtnl_nat_set_mask(struct rtnl_act *act, in_addr_t bitmask) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) return -NLE_NOMEM; nat->mask = bitmask; return NLE_SUCCESS; } int rtnl_nat_get_mask(struct rtnl_act *act, in_addr_t *bitmask) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) return -NLE_NOATTR; *bitmask = nat->mask; return NLE_SUCCESS; } /** * Set flags for a netlink NAT action object * @arg act Action object * @arg flags TCA_NAT_FLAG_* flags. * * Currently only TCA_NAT_FLAG_EGRESS is defined. Selects NAT on * egress/IP src if set, ingress/IP dst otherwise. * * @return 0 on success or negative error code in case of an error. */ int rtnl_nat_set_flags(struct rtnl_act *act, uint32_t flags) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) return -NLE_NOMEM; nat->flags = flags; return NLE_SUCCESS; } int rtnl_nat_get_flags(struct rtnl_act *act, uint32_t *flags) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) return -NLE_NOATTR; *flags = nat->flags; return NLE_SUCCESS; } int rtnl_nat_set_action(struct rtnl_act *act, int action) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data(TC_CAST(act)))) return -NLE_NOMEM; if (action < TC_ACT_UNSPEC) return -NLE_INVAL; nat->action = action; return NLE_SUCCESS; } int rtnl_nat_get_action(struct rtnl_act *act, int *action) { struct tc_nat *nat; if (!(nat = (struct tc_nat *)rtnl_tc_data_peek(TC_CAST(act)))) return -NLE_NOATTR; *action = nat->action; return NLE_SUCCESS; } /** * @} */ static struct rtnl_tc_ops nat_ops = { .to_kind = "nat", .to_type = RTNL_TC_TYPE_ACT, .to_size = sizeof(struct tc_nat), .to_msg_parser = nat_msg_parser, .to_free_data = nat_free_data, .to_clone = NULL, .to_msg_fill = nat_msg_fill, .to_dump = { [NL_DUMP_LINE] = nat_dump_line, }, }; static void __init nat_init(void) { rtnl_tc_register(&nat_ops); } static void __exit nat_exit(void) { rtnl_tc_unregister(&nat_ops); } /** * @} */