linux/net/netfilter/nf_tables_trace.c
<<
>>
Prefs
   1/*
   2 * (C) 2015 Red Hat GmbH
   3 * Author: Florian Westphal <fw@strlen.de>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/static_key.h>
  12#include <linux/hash.h>
  13#include <linux/jhash.h>
  14#include <linux/if_vlan.h>
  15#include <linux/init.h>
  16#include <linux/skbuff.h>
  17#include <linux/netlink.h>
  18#include <linux/netfilter.h>
  19#include <linux/netfilter/nfnetlink.h>
  20#include <linux/netfilter/nf_tables.h>
  21#include <net/netfilter/nf_tables_core.h>
  22#include <net/netfilter/nf_tables.h>
  23
  24#define NFT_TRACETYPE_LL_HSIZE          20
  25#define NFT_TRACETYPE_NETWORK_HSIZE     40
  26#define NFT_TRACETYPE_TRANSPORT_HSIZE   20
  27
  28DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
  29EXPORT_SYMBOL_GPL(nft_trace_enabled);
  30
  31static int trace_fill_id(struct sk_buff *nlskb, struct sk_buff *skb)
  32{
  33        __be32 id;
  34
  35        /* using skb address as ID results in a limited number of
  36         * values (and quick reuse).
  37         *
  38         * So we attempt to use as many skb members that will not
  39         * change while skb is with netfilter.
  40         */
  41        id = (__be32)jhash_2words(hash32_ptr(skb), skb_get_hash(skb),
  42                                  skb->skb_iif);
  43
  44        return nla_put_be32(nlskb, NFTA_TRACE_ID, id);
  45}
  46
  47static int trace_fill_header(struct sk_buff *nlskb, u16 type,
  48                             const struct sk_buff *skb,
  49                             int off, unsigned int len)
  50{
  51        struct nlattr *nla;
  52
  53        if (len == 0)
  54                return 0;
  55
  56        nla = nla_reserve(nlskb, type, len);
  57        if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
  58                return -1;
  59
  60        return 0;
  61}
  62
  63static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
  64                                   const struct sk_buff *skb)
  65{
  66        struct vlan_ethhdr veth;
  67        int off;
  68
  69        BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
  70
  71        off = skb_mac_header(skb) - skb->data;
  72        if (off != -ETH_HLEN)
  73                return -1;
  74
  75        if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
  76                return -1;
  77
  78        veth.h_vlan_proto = skb->vlan_proto;
  79        veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
  80        veth.h_vlan_encapsulated_proto = skb->protocol;
  81
  82        return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
  83}
  84
  85static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
  86                                  const struct net_device *indev,
  87                                  const struct net_device *outdev)
  88{
  89        if (indev) {
  90                if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
  91                                 htonl(indev->ifindex)))
  92                        return -1;
  93
  94                if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
  95                                 htons(indev->type)))
  96                        return -1;
  97        }
  98
  99        if (outdev) {
 100                if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
 101                                 htonl(outdev->ifindex)))
 102                        return -1;
 103
 104                if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
 105                                 htons(outdev->type)))
 106                        return -1;
 107        }
 108
 109        return 0;
 110}
 111
 112static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
 113                                  const struct nft_pktinfo *pkt)
 114{
 115        const struct sk_buff *skb = pkt->skb;
 116        int off = skb_network_offset(skb);
 117        unsigned int len, nh_end;
 118
 119        nh_end = pkt->tprot_set ? pkt->xt.thoff : skb->len;
 120        len = min_t(unsigned int, nh_end - skb_network_offset(skb),
 121                    NFT_TRACETYPE_NETWORK_HSIZE);
 122        if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
 123                return -1;
 124
 125        if (pkt->tprot_set) {
 126                len = min_t(unsigned int, skb->len - pkt->xt.thoff,
 127                            NFT_TRACETYPE_TRANSPORT_HSIZE);
 128                if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
 129                                      pkt->xt.thoff, len))
 130                        return -1;
 131        }
 132
 133        if (!skb_mac_header_was_set(skb))
 134                return 0;
 135
 136        if (skb_vlan_tag_get(skb))
 137                return nf_trace_fill_ll_header(nlskb, skb);
 138
 139        off = skb_mac_header(skb) - skb->data;
 140        len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
 141        return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
 142                                 skb, off, len);
 143}
 144
 145static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
 146                                   const struct nft_traceinfo *info)
 147{
 148        if (!info->rule)
 149                return 0;
 150
 151        /* a continue verdict with ->type == RETURN means that this is
 152         * an implicit return (end of chain reached).
 153         *
 154         * Since no rule matched, the ->rule pointer is invalid.
 155         */
 156        if (info->type == NFT_TRACETYPE_RETURN &&
 157            info->verdict->code == NFT_CONTINUE)
 158                return 0;
 159
 160        return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
 161                            cpu_to_be64(info->rule->handle),
 162                            NFTA_TRACE_PAD);
 163}
 164
 165void nft_trace_notify(struct nft_traceinfo *info)
 166{
 167        const struct nft_pktinfo *pkt = info->pkt;
 168        struct nfgenmsg *nfmsg;
 169        struct nlmsghdr *nlh;
 170        struct sk_buff *skb;
 171        unsigned int size;
 172        int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE;
 173
 174        if (!nfnetlink_has_listeners(pkt->net, NFNLGRP_NFTRACE))
 175                return;
 176
 177        size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
 178                nla_total_size(NFT_TABLE_MAXNAMELEN) +
 179                nla_total_size(NFT_CHAIN_MAXNAMELEN) +
 180                nla_total_size_64bit(sizeof(__be64)) +  /* rule handle */
 181                nla_total_size(sizeof(__be32)) +        /* trace type */
 182                nla_total_size(0) +                     /* VERDICT, nested */
 183                        nla_total_size(sizeof(u32)) +   /* verdict code */
 184                        nla_total_size(NFT_CHAIN_MAXNAMELEN) + /* jump target */
 185                nla_total_size(sizeof(u32)) +           /* id */
 186                nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
 187                nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
 188                nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
 189                nla_total_size(sizeof(u32)) +           /* iif */
 190                nla_total_size(sizeof(__be16)) +        /* iiftype */
 191                nla_total_size(sizeof(u32)) +           /* oif */
 192                nla_total_size(sizeof(__be16)) +        /* oiftype */
 193                nla_total_size(sizeof(u32)) +           /* mark */
 194                nla_total_size(sizeof(u32)) +           /* nfproto */
 195                nla_total_size(sizeof(u32));            /* policy */
 196
 197        skb = nlmsg_new(size, GFP_ATOMIC);
 198        if (!skb)
 199                return;
 200
 201        nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0);
 202        if (!nlh)
 203                goto nla_put_failure;
 204
 205        nfmsg = nlmsg_data(nlh);
 206        nfmsg->nfgen_family     = info->basechain->type->family;
 207        nfmsg->version          = NFNETLINK_V0;
 208        nfmsg->res_id           = 0;
 209
 210        if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(pkt->pf)))
 211                goto nla_put_failure;
 212
 213        if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
 214                goto nla_put_failure;
 215
 216        if (trace_fill_id(skb, pkt->skb))
 217                goto nla_put_failure;
 218
 219        if (info->chain) {
 220                if (nla_put_string(skb, NFTA_TRACE_CHAIN,
 221                                   info->chain->name))
 222                        goto nla_put_failure;
 223                if (nla_put_string(skb, NFTA_TRACE_TABLE,
 224                                   info->chain->table->name))
 225                        goto nla_put_failure;
 226        }
 227
 228        if (nf_trace_fill_rule_info(skb, info))
 229                goto nla_put_failure;
 230
 231        switch (info->type) {
 232        case NFT_TRACETYPE_UNSPEC:
 233        case __NFT_TRACETYPE_MAX:
 234                break;
 235        case NFT_TRACETYPE_RETURN:
 236        case NFT_TRACETYPE_RULE:
 237                if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
 238                        goto nla_put_failure;
 239                break;
 240        case NFT_TRACETYPE_POLICY:
 241                if (nla_put_be32(skb, NFTA_TRACE_POLICY,
 242                                 htonl(info->basechain->policy)))
 243                        goto nla_put_failure;
 244                break;
 245        }
 246
 247        if (pkt->skb->mark &&
 248            nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark)))
 249                goto nla_put_failure;
 250
 251        if (!info->packet_dumped) {
 252                if (nf_trace_fill_dev_info(skb, pkt->in, pkt->out))
 253                        goto nla_put_failure;
 254
 255                if (nf_trace_fill_pkt_info(skb, pkt))
 256                        goto nla_put_failure;
 257                info->packet_dumped = true;
 258        }
 259
 260        nlmsg_end(skb, nlh);
 261        nfnetlink_send(skb, pkt->net, 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
 262        return;
 263
 264 nla_put_failure:
 265        WARN_ON_ONCE(1);
 266        kfree_skb(skb);
 267}
 268
 269void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
 270                    const struct nft_verdict *verdict,
 271                    const struct nft_chain *chain)
 272{
 273        info->basechain = nft_base_chain(chain);
 274        info->trace = true;
 275        info->packet_dumped = false;
 276        info->pkt = pkt;
 277        info->verdict = verdict;
 278}
 279