Thomas Gleixner | d2912cb | 2019-06-04 10:11:33 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com> |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 4 | */ |
| 5 | |
| 6 | #include <linux/kernel.h> |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 7 | #include <linux/netlink.h> |
| 8 | #include <linux/netfilter.h> |
| 9 | #include <linux/netfilter/nf_tables.h> |
| 10 | #include <net/dst.h> |
| 11 | #include <net/ip6_route.h> |
| 12 | #include <net/route.h> |
| 13 | #include <net/netfilter/nf_tables.h> |
| 14 | #include <net/netfilter/nf_tables_core.h> |
| 15 | |
| 16 | struct nft_rt { |
| 17 | enum nft_rt_keys key:8; |
Pablo Neira Ayuso | 345023b | 2021-01-25 18:27:22 +0100 | [diff] [blame] | 18 | u8 dreg; |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 19 | }; |
| 20 | |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 21 | static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst) |
| 22 | { |
| 23 | u32 minlen = sizeof(struct ipv6hdr), mtu = dst_mtu(skbdst); |
| 24 | const struct sk_buff *skb = pkt->skb; |
Pablo Neira Ayuso | 3f87c08 | 2017-11-27 22:29:52 +0100 | [diff] [blame] | 25 | struct dst_entry *dst = NULL; |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 26 | struct flowi fl; |
| 27 | |
| 28 | memset(&fl, 0, sizeof(fl)); |
| 29 | |
| 30 | switch (nft_pf(pkt)) { |
| 31 | case NFPROTO_IPV4: |
| 32 | fl.u.ip4.daddr = ip_hdr(skb)->saddr; |
Florian Westphal | 1aff647 | 2017-08-28 17:00:12 +0200 | [diff] [blame] | 33 | minlen = sizeof(struct iphdr) + sizeof(struct tcphdr); |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 34 | break; |
| 35 | case NFPROTO_IPV6: |
| 36 | fl.u.ip6.daddr = ipv6_hdr(skb)->saddr; |
Florian Westphal | 1aff647 | 2017-08-28 17:00:12 +0200 | [diff] [blame] | 37 | minlen = sizeof(struct ipv6hdr) + sizeof(struct tcphdr); |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 38 | break; |
| 39 | } |
| 40 | |
Pablo Neira Ayuso | 3f87c08 | 2017-11-27 22:29:52 +0100 | [diff] [blame] | 41 | nf_route(nft_net(pkt), &dst, &fl, false, nft_pf(pkt)); |
| 42 | if (dst) { |
| 43 | mtu = min(mtu, dst_mtu(dst)); |
| 44 | dst_release(dst); |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | if (mtu <= minlen || mtu > 0xffff) |
| 48 | return TCP_MSS_DEFAULT; |
| 49 | |
| 50 | return mtu - minlen; |
| 51 | } |
| 52 | |
Florian Westphal | 10870dd | 2019-01-08 17:35:34 +0100 | [diff] [blame] | 53 | void nft_rt_get_eval(const struct nft_expr *expr, |
| 54 | struct nft_regs *regs, |
| 55 | const struct nft_pktinfo *pkt) |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 56 | { |
| 57 | const struct nft_rt *priv = nft_expr_priv(expr); |
| 58 | const struct sk_buff *skb = pkt->skb; |
| 59 | u32 *dest = ®s->data[priv->dreg]; |
| 60 | const struct dst_entry *dst; |
| 61 | |
| 62 | dst = skb_dst(skb); |
| 63 | if (!dst) |
| 64 | goto err; |
| 65 | |
| 66 | switch (priv->key) { |
| 67 | #ifdef CONFIG_IP_ROUTE_CLASSID |
| 68 | case NFT_RT_CLASSID: |
| 69 | *dest = dst->tclassid; |
| 70 | break; |
| 71 | #endif |
| 72 | case NFT_RT_NEXTHOP4: |
Pablo Neira Ayuso | 0e5a1c7 | 2016-11-03 10:56:26 +0100 | [diff] [blame] | 73 | if (nft_pf(pkt) != NFPROTO_IPV4) |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 74 | goto err; |
| 75 | |
Florian Westphal | 5fd02eb | 2017-08-21 16:19:26 +0200 | [diff] [blame] | 76 | *dest = (__force u32)rt_nexthop((const struct rtable *)dst, |
| 77 | ip_hdr(skb)->daddr); |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 78 | break; |
| 79 | case NFT_RT_NEXTHOP6: |
Pablo Neira Ayuso | 0e5a1c7 | 2016-11-03 10:56:26 +0100 | [diff] [blame] | 80 | if (nft_pf(pkt) != NFPROTO_IPV6) |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 81 | goto err; |
| 82 | |
| 83 | memcpy(dest, rt6_nexthop((struct rt6_info *)dst, |
| 84 | &ipv6_hdr(skb)->daddr), |
| 85 | sizeof(struct in6_addr)); |
| 86 | break; |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 87 | case NFT_RT_TCPMSS: |
| 88 | nft_reg_store16(dest, get_tcpmss(pkt, dst)); |
| 89 | break; |
Florian Westphal | 02b408f | 2018-08-29 00:19:00 +0200 | [diff] [blame] | 90 | #ifdef CONFIG_XFRM |
| 91 | case NFT_RT_XFRM: |
| 92 | nft_reg_store8(dest, !!dst->xfrm); |
| 93 | break; |
| 94 | #endif |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 95 | default: |
| 96 | WARN_ON(1); |
| 97 | goto err; |
| 98 | } |
| 99 | return; |
| 100 | |
| 101 | err: |
| 102 | regs->verdict.code = NFT_BREAK; |
| 103 | } |
| 104 | |
Florian Westphal | 5fd02eb | 2017-08-21 16:19:26 +0200 | [diff] [blame] | 105 | static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 106 | [NFTA_RT_DREG] = { .type = NLA_U32 }, |
| 107 | [NFTA_RT_KEY] = { .type = NLA_U32 }, |
| 108 | }; |
| 109 | |
stephen hemminger | cad4394 | 2017-05-19 09:29:42 -0700 | [diff] [blame] | 110 | static int nft_rt_get_init(const struct nft_ctx *ctx, |
| 111 | const struct nft_expr *expr, |
| 112 | const struct nlattr * const tb[]) |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 113 | { |
| 114 | struct nft_rt *priv = nft_expr_priv(expr); |
| 115 | unsigned int len; |
| 116 | |
| 117 | if (tb[NFTA_RT_KEY] == NULL || |
| 118 | tb[NFTA_RT_DREG] == NULL) |
| 119 | return -EINVAL; |
| 120 | |
| 121 | priv->key = ntohl(nla_get_be32(tb[NFTA_RT_KEY])); |
| 122 | switch (priv->key) { |
| 123 | #ifdef CONFIG_IP_ROUTE_CLASSID |
| 124 | case NFT_RT_CLASSID: |
| 125 | #endif |
| 126 | case NFT_RT_NEXTHOP4: |
| 127 | len = sizeof(u32); |
| 128 | break; |
| 129 | case NFT_RT_NEXTHOP6: |
| 130 | len = sizeof(struct in6_addr); |
| 131 | break; |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 132 | case NFT_RT_TCPMSS: |
| 133 | len = sizeof(u16); |
| 134 | break; |
Florian Westphal | 02b408f | 2018-08-29 00:19:00 +0200 | [diff] [blame] | 135 | #ifdef CONFIG_XFRM |
| 136 | case NFT_RT_XFRM: |
| 137 | len = sizeof(u8); |
| 138 | break; |
| 139 | #endif |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 140 | default: |
| 141 | return -EOPNOTSUPP; |
| 142 | } |
| 143 | |
Pablo Neira Ayuso | 345023b | 2021-01-25 18:27:22 +0100 | [diff] [blame] | 144 | return nft_parse_register_store(ctx, tb[NFTA_RT_DREG], &priv->dreg, |
| 145 | NULL, NFT_DATA_VALUE, len); |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 146 | } |
| 147 | |
stephen hemminger | cad4394 | 2017-05-19 09:29:42 -0700 | [diff] [blame] | 148 | static int nft_rt_get_dump(struct sk_buff *skb, |
Phil Sutter | 7d34aa3 | 2022-10-14 23:45:58 +0200 | [diff] [blame] | 149 | const struct nft_expr *expr, bool reset) |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 150 | { |
| 151 | const struct nft_rt *priv = nft_expr_priv(expr); |
| 152 | |
| 153 | if (nla_put_be32(skb, NFTA_RT_KEY, htonl(priv->key))) |
| 154 | goto nla_put_failure; |
| 155 | if (nft_dump_register(skb, NFTA_RT_DREG, priv->dreg)) |
| 156 | goto nla_put_failure; |
| 157 | return 0; |
| 158 | |
| 159 | nla_put_failure: |
| 160 | return -1; |
| 161 | } |
| 162 | |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 163 | static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, |
| 164 | const struct nft_data **data) |
| 165 | { |
| 166 | const struct nft_rt *priv = nft_expr_priv(expr); |
| 167 | unsigned int hooks; |
| 168 | |
| 169 | switch (priv->key) { |
| 170 | case NFT_RT_NEXTHOP4: |
| 171 | case NFT_RT_NEXTHOP6: |
| 172 | case NFT_RT_CLASSID: |
Florian Westphal | 02b408f | 2018-08-29 00:19:00 +0200 | [diff] [blame] | 173 | case NFT_RT_XFRM: |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 174 | return 0; |
| 175 | case NFT_RT_TCPMSS: |
| 176 | hooks = (1 << NF_INET_FORWARD) | |
| 177 | (1 << NF_INET_LOCAL_OUT) | |
| 178 | (1 << NF_INET_POST_ROUTING); |
| 179 | break; |
| 180 | default: |
| 181 | return -EINVAL; |
| 182 | } |
| 183 | |
| 184 | return nft_chain_validate_hooks(ctx->chain, hooks); |
| 185 | } |
| 186 | |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 187 | static const struct nft_expr_ops nft_rt_get_ops = { |
| 188 | .type = &nft_rt_type, |
| 189 | .size = NFT_EXPR_SIZE(sizeof(struct nft_rt)), |
| 190 | .eval = nft_rt_get_eval, |
| 191 | .init = nft_rt_get_init, |
| 192 | .dump = nft_rt_get_dump, |
Florian Westphal | 6b5dc98 | 2017-08-08 15:48:04 +0200 | [diff] [blame] | 193 | .validate = nft_rt_validate, |
Pablo Neira Ayuso | b2d3065 | 2022-03-14 18:23:00 +0100 | [diff] [blame] | 194 | .reduce = NFT_REDUCE_READONLY, |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 195 | }; |
| 196 | |
Florian Westphal | ae1bc6a | 2018-04-16 19:15:55 +0200 | [diff] [blame] | 197 | struct nft_expr_type nft_rt_type __read_mostly = { |
Anders K. Pedersen | 2fa8419 | 2016-10-28 05:54:15 +0000 | [diff] [blame] | 198 | .name = "rt", |
| 199 | .ops = &nft_rt_get_ops, |
| 200 | .policy = nft_rt_policy, |
| 201 | .maxattr = NFTA_RT_MAX, |
| 202 | .owner = THIS_MODULE, |
| 203 | }; |