root/net/netfilter/nft_flow_offload.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nft_flow_route
  2. nft_flow_offload_skip
  3. nft_flow_offload_eval
  4. nft_flow_offload_validate
  5. nft_flow_offload_init
  6. nft_flow_offload_deactivate
  7. nft_flow_offload_activate
  8. nft_flow_offload_destroy
  9. nft_flow_offload_dump
  10. flow_offload_netdev_event
  11. nft_flow_offload_module_init
  12. nft_flow_offload_module_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 #include <linux/kernel.h>
   3 #include <linux/module.h>
   4 #include <linux/init.h>
   5 #include <linux/netlink.h>
   6 #include <linux/netfilter.h>
   7 #include <linux/workqueue.h>
   8 #include <linux/spinlock.h>
   9 #include <linux/netfilter/nf_conntrack_common.h>
  10 #include <linux/netfilter/nf_tables.h>
  11 #include <net/ip.h> /* for ipv4 options. */
  12 #include <net/netfilter/nf_tables.h>
  13 #include <net/netfilter/nf_tables_core.h>
  14 #include <net/netfilter/nf_conntrack_core.h>
  15 #include <net/netfilter/nf_conntrack_extend.h>
  16 #include <net/netfilter/nf_flow_table.h>
  17 
  18 struct nft_flow_offload {
  19         struct nft_flowtable    *flowtable;
  20 };
  21 
  22 static int nft_flow_route(const struct nft_pktinfo *pkt,
  23                           const struct nf_conn *ct,
  24                           struct nf_flow_route *route,
  25                           enum ip_conntrack_dir dir)
  26 {
  27         struct dst_entry *this_dst = skb_dst(pkt->skb);
  28         struct dst_entry *other_dst = NULL;
  29         struct flowi fl;
  30 
  31         memset(&fl, 0, sizeof(fl));
  32         switch (nft_pf(pkt)) {
  33         case NFPROTO_IPV4:
  34                 fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
  35                 fl.u.ip4.flowi4_oif = nft_in(pkt)->ifindex;
  36                 break;
  37         case NFPROTO_IPV6:
  38                 fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
  39                 fl.u.ip6.flowi6_oif = nft_in(pkt)->ifindex;
  40                 break;
  41         }
  42 
  43         nf_route(nft_net(pkt), &other_dst, &fl, false, nft_pf(pkt));
  44         if (!other_dst)
  45                 return -ENOENT;
  46 
  47         route->tuple[dir].dst           = this_dst;
  48         route->tuple[!dir].dst          = other_dst;
  49 
  50         return 0;
  51 }
  52 
  53 static bool nft_flow_offload_skip(struct sk_buff *skb, int family)
  54 {
  55         if (skb_sec_path(skb))
  56                 return true;
  57 
  58         if (family == NFPROTO_IPV4) {
  59                 const struct ip_options *opt;
  60 
  61                 opt = &(IPCB(skb)->opt);
  62 
  63                 if (unlikely(opt->optlen))
  64                         return true;
  65         }
  66 
  67         return false;
  68 }
  69 
  70 static void nft_flow_offload_eval(const struct nft_expr *expr,
  71                                   struct nft_regs *regs,
  72                                   const struct nft_pktinfo *pkt)
  73 {
  74         struct nft_flow_offload *priv = nft_expr_priv(expr);
  75         struct nf_flowtable *flowtable = &priv->flowtable->data;
  76         struct tcphdr _tcph, *tcph = NULL;
  77         enum ip_conntrack_info ctinfo;
  78         struct nf_flow_route route;
  79         struct flow_offload *flow;
  80         enum ip_conntrack_dir dir;
  81         struct nf_conn *ct;
  82         int ret;
  83 
  84         if (nft_flow_offload_skip(pkt->skb, nft_pf(pkt)))
  85                 goto out;
  86 
  87         ct = nf_ct_get(pkt->skb, &ctinfo);
  88         if (!ct)
  89                 goto out;
  90 
  91         switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
  92         case IPPROTO_TCP:
  93                 tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff,
  94                                           sizeof(_tcph), &_tcph);
  95                 if (unlikely(!tcph || tcph->fin || tcph->rst))
  96                         goto out;
  97                 break;
  98         case IPPROTO_UDP:
  99                 break;
 100         default:
 101                 goto out;
 102         }
 103 
 104         if (nf_ct_ext_exist(ct, NF_CT_EXT_HELPER) ||
 105             ct->status & IPS_SEQ_ADJUST)
 106                 goto out;
 107 
 108         if (!nf_ct_is_confirmed(ct))
 109                 goto out;
 110 
 111         if (test_and_set_bit(IPS_OFFLOAD_BIT, &ct->status))
 112                 goto out;
 113 
 114         dir = CTINFO2DIR(ctinfo);
 115         if (nft_flow_route(pkt, ct, &route, dir) < 0)
 116                 goto err_flow_route;
 117 
 118         flow = flow_offload_alloc(ct, &route);
 119         if (!flow)
 120                 goto err_flow_alloc;
 121 
 122         if (tcph) {
 123                 ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
 124                 ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
 125         }
 126 
 127         ret = flow_offload_add(flowtable, flow);
 128         if (ret < 0)
 129                 goto err_flow_add;
 130 
 131         dst_release(route.tuple[!dir].dst);
 132         return;
 133 
 134 err_flow_add:
 135         flow_offload_free(flow);
 136 err_flow_alloc:
 137         dst_release(route.tuple[!dir].dst);
 138 err_flow_route:
 139         clear_bit(IPS_OFFLOAD_BIT, &ct->status);
 140 out:
 141         regs->verdict.code = NFT_BREAK;
 142 }
 143 
 144 static int nft_flow_offload_validate(const struct nft_ctx *ctx,
 145                                      const struct nft_expr *expr,
 146                                      const struct nft_data **data)
 147 {
 148         unsigned int hook_mask = (1 << NF_INET_FORWARD);
 149 
 150         return nft_chain_validate_hooks(ctx->chain, hook_mask);
 151 }
 152 
 153 static const struct nla_policy nft_flow_offload_policy[NFTA_FLOW_MAX + 1] = {
 154         [NFTA_FLOW_TABLE_NAME]  = { .type = NLA_STRING,
 155                                     .len = NFT_NAME_MAXLEN - 1 },
 156 };
 157 
 158 static int nft_flow_offload_init(const struct nft_ctx *ctx,
 159                                  const struct nft_expr *expr,
 160                                  const struct nlattr * const tb[])
 161 {
 162         struct nft_flow_offload *priv = nft_expr_priv(expr);
 163         u8 genmask = nft_genmask_next(ctx->net);
 164         struct nft_flowtable *flowtable;
 165 
 166         if (!tb[NFTA_FLOW_TABLE_NAME])
 167                 return -EINVAL;
 168 
 169         flowtable = nft_flowtable_lookup(ctx->table, tb[NFTA_FLOW_TABLE_NAME],
 170                                          genmask);
 171         if (IS_ERR(flowtable))
 172                 return PTR_ERR(flowtable);
 173 
 174         priv->flowtable = flowtable;
 175         flowtable->use++;
 176 
 177         return nf_ct_netns_get(ctx->net, ctx->family);
 178 }
 179 
 180 static void nft_flow_offload_deactivate(const struct nft_ctx *ctx,
 181                                         const struct nft_expr *expr,
 182                                         enum nft_trans_phase phase)
 183 {
 184         struct nft_flow_offload *priv = nft_expr_priv(expr);
 185 
 186         nf_tables_deactivate_flowtable(ctx, priv->flowtable, phase);
 187 }
 188 
 189 static void nft_flow_offload_activate(const struct nft_ctx *ctx,
 190                                       const struct nft_expr *expr)
 191 {
 192         struct nft_flow_offload *priv = nft_expr_priv(expr);
 193 
 194         priv->flowtable->use++;
 195 }
 196 
 197 static void nft_flow_offload_destroy(const struct nft_ctx *ctx,
 198                                      const struct nft_expr *expr)
 199 {
 200         nf_ct_netns_put(ctx->net, ctx->family);
 201 }
 202 
 203 static int nft_flow_offload_dump(struct sk_buff *skb, const struct nft_expr *expr)
 204 {
 205         struct nft_flow_offload *priv = nft_expr_priv(expr);
 206 
 207         if (nla_put_string(skb, NFTA_FLOW_TABLE_NAME, priv->flowtable->name))
 208                 goto nla_put_failure;
 209 
 210         return 0;
 211 
 212 nla_put_failure:
 213         return -1;
 214 }
 215 
 216 static struct nft_expr_type nft_flow_offload_type;
 217 static const struct nft_expr_ops nft_flow_offload_ops = {
 218         .type           = &nft_flow_offload_type,
 219         .size           = NFT_EXPR_SIZE(sizeof(struct nft_flow_offload)),
 220         .eval           = nft_flow_offload_eval,
 221         .init           = nft_flow_offload_init,
 222         .activate       = nft_flow_offload_activate,
 223         .deactivate     = nft_flow_offload_deactivate,
 224         .destroy        = nft_flow_offload_destroy,
 225         .validate       = nft_flow_offload_validate,
 226         .dump           = nft_flow_offload_dump,
 227 };
 228 
 229 static struct nft_expr_type nft_flow_offload_type __read_mostly = {
 230         .name           = "flow_offload",
 231         .ops            = &nft_flow_offload_ops,
 232         .policy         = nft_flow_offload_policy,
 233         .maxattr        = NFTA_FLOW_MAX,
 234         .owner          = THIS_MODULE,
 235 };
 236 
 237 static int flow_offload_netdev_event(struct notifier_block *this,
 238                                      unsigned long event, void *ptr)
 239 {
 240         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 241 
 242         if (event != NETDEV_DOWN)
 243                 return NOTIFY_DONE;
 244 
 245         nf_flow_table_cleanup(dev);
 246 
 247         return NOTIFY_DONE;
 248 }
 249 
 250 static struct notifier_block flow_offload_netdev_notifier = {
 251         .notifier_call  = flow_offload_netdev_event,
 252 };
 253 
 254 static int __init nft_flow_offload_module_init(void)
 255 {
 256         int err;
 257 
 258         err = register_netdevice_notifier(&flow_offload_netdev_notifier);
 259         if (err)
 260                 goto err;
 261 
 262         err = nft_register_expr(&nft_flow_offload_type);
 263         if (err < 0)
 264                 goto register_expr;
 265 
 266         return 0;
 267 
 268 register_expr:
 269         unregister_netdevice_notifier(&flow_offload_netdev_notifier);
 270 err:
 271         return err;
 272 }
 273 
 274 static void __exit nft_flow_offload_module_exit(void)
 275 {
 276         nft_unregister_expr(&nft_flow_offload_type);
 277         unregister_netdevice_notifier(&flow_offload_netdev_notifier);
 278 }
 279 
 280 module_init(nft_flow_offload_module_init);
 281 module_exit(nft_flow_offload_module_exit);
 282 
 283 MODULE_LICENSE("GPL");
 284 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 285 MODULE_ALIAS_NFT_EXPR("flow_offload");

/* [<][>][^][v][top][bottom][index][help] */