Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * (C) 2015 Red Hat GmbH
0004  * Author: Florian Westphal <fw@strlen.de>
0005  */
0006 
0007 #include <linux/module.h>
0008 #include <linux/static_key.h>
0009 #include <linux/hash.h>
0010 #include <linux/siphash.h>
0011 #include <linux/if_vlan.h>
0012 #include <linux/init.h>
0013 #include <linux/skbuff.h>
0014 #include <linux/netlink.h>
0015 #include <linux/netfilter.h>
0016 #include <linux/netfilter/nfnetlink.h>
0017 #include <linux/netfilter/nf_tables.h>
0018 #include <net/netfilter/nf_tables_core.h>
0019 #include <net/netfilter/nf_tables.h>
0020 
0021 #define NFT_TRACETYPE_LL_HSIZE      20
0022 #define NFT_TRACETYPE_NETWORK_HSIZE 40
0023 #define NFT_TRACETYPE_TRANSPORT_HSIZE   20
0024 
0025 DEFINE_STATIC_KEY_FALSE(nft_trace_enabled);
0026 EXPORT_SYMBOL_GPL(nft_trace_enabled);
0027 
0028 static int trace_fill_header(struct sk_buff *nlskb, u16 type,
0029                  const struct sk_buff *skb,
0030                  int off, unsigned int len)
0031 {
0032     struct nlattr *nla;
0033 
0034     if (len == 0)
0035         return 0;
0036 
0037     nla = nla_reserve(nlskb, type, len);
0038     if (!nla || skb_copy_bits(skb, off, nla_data(nla), len))
0039         return -1;
0040 
0041     return 0;
0042 }
0043 
0044 static int nf_trace_fill_ll_header(struct sk_buff *nlskb,
0045                    const struct sk_buff *skb)
0046 {
0047     struct vlan_ethhdr veth;
0048     int off;
0049 
0050     BUILD_BUG_ON(sizeof(veth) > NFT_TRACETYPE_LL_HSIZE);
0051 
0052     off = skb_mac_header(skb) - skb->data;
0053     if (off != -ETH_HLEN)
0054         return -1;
0055 
0056     if (skb_copy_bits(skb, off, &veth, ETH_HLEN))
0057         return -1;
0058 
0059     veth.h_vlan_proto = skb->vlan_proto;
0060     veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb));
0061     veth.h_vlan_encapsulated_proto = skb->protocol;
0062 
0063     return nla_put(nlskb, NFTA_TRACE_LL_HEADER, sizeof(veth), &veth);
0064 }
0065 
0066 static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
0067                   const struct net_device *indev,
0068                   const struct net_device *outdev)
0069 {
0070     if (indev) {
0071         if (nla_put_be32(nlskb, NFTA_TRACE_IIF,
0072                  htonl(indev->ifindex)))
0073             return -1;
0074 
0075         if (nla_put_be16(nlskb, NFTA_TRACE_IIFTYPE,
0076                  htons(indev->type)))
0077             return -1;
0078     }
0079 
0080     if (outdev) {
0081         if (nla_put_be32(nlskb, NFTA_TRACE_OIF,
0082                  htonl(outdev->ifindex)))
0083             return -1;
0084 
0085         if (nla_put_be16(nlskb, NFTA_TRACE_OIFTYPE,
0086                  htons(outdev->type)))
0087             return -1;
0088     }
0089 
0090     return 0;
0091 }
0092 
0093 static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
0094                   const struct nft_pktinfo *pkt)
0095 {
0096     const struct sk_buff *skb = pkt->skb;
0097     int off = skb_network_offset(skb);
0098     unsigned int len, nh_end;
0099 
0100     nh_end = pkt->flags & NFT_PKTINFO_L4PROTO ? nft_thoff(pkt) : skb->len;
0101     len = min_t(unsigned int, nh_end - skb_network_offset(skb),
0102             NFT_TRACETYPE_NETWORK_HSIZE);
0103     if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len))
0104         return -1;
0105 
0106     if (pkt->flags & NFT_PKTINFO_L4PROTO) {
0107         len = min_t(unsigned int, skb->len - nft_thoff(pkt),
0108                 NFT_TRACETYPE_TRANSPORT_HSIZE);
0109         if (trace_fill_header(nlskb, NFTA_TRACE_TRANSPORT_HEADER, skb,
0110                       nft_thoff(pkt), len))
0111             return -1;
0112     }
0113 
0114     if (!skb_mac_header_was_set(skb))
0115         return 0;
0116 
0117     if (skb_vlan_tag_get(skb))
0118         return nf_trace_fill_ll_header(nlskb, skb);
0119 
0120     off = skb_mac_header(skb) - skb->data;
0121     len = min_t(unsigned int, -off, NFT_TRACETYPE_LL_HSIZE);
0122     return trace_fill_header(nlskb, NFTA_TRACE_LL_HEADER,
0123                  skb, off, len);
0124 }
0125 
0126 static int nf_trace_fill_rule_info(struct sk_buff *nlskb,
0127                    const struct nft_traceinfo *info)
0128 {
0129     if (!info->rule || info->rule->is_last)
0130         return 0;
0131 
0132     /* a continue verdict with ->type == RETURN means that this is
0133      * an implicit return (end of chain reached).
0134      *
0135      * Since no rule matched, the ->rule pointer is invalid.
0136      */
0137     if (info->type == NFT_TRACETYPE_RETURN &&
0138         info->verdict->code == NFT_CONTINUE)
0139         return 0;
0140 
0141     return nla_put_be64(nlskb, NFTA_TRACE_RULE_HANDLE,
0142                 cpu_to_be64(info->rule->handle),
0143                 NFTA_TRACE_PAD);
0144 }
0145 
0146 static bool nft_trace_have_verdict_chain(struct nft_traceinfo *info)
0147 {
0148     switch (info->type) {
0149     case NFT_TRACETYPE_RETURN:
0150     case NFT_TRACETYPE_RULE:
0151         break;
0152     default:
0153         return false;
0154     }
0155 
0156     switch (info->verdict->code) {
0157     case NFT_JUMP:
0158     case NFT_GOTO:
0159         break;
0160     default:
0161         return false;
0162     }
0163 
0164     return true;
0165 }
0166 
0167 void nft_trace_notify(struct nft_traceinfo *info)
0168 {
0169     const struct nft_pktinfo *pkt = info->pkt;
0170     struct nlmsghdr *nlh;
0171     struct sk_buff *skb;
0172     unsigned int size;
0173     u32 mark = 0;
0174     u16 event;
0175 
0176     if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE))
0177         return;
0178 
0179     size = nlmsg_total_size(sizeof(struct nfgenmsg)) +
0180         nla_total_size(strlen(info->chain->table->name)) +
0181         nla_total_size(strlen(info->chain->name)) +
0182         nla_total_size_64bit(sizeof(__be64)) +  /* rule handle */
0183         nla_total_size(sizeof(__be32)) +    /* trace type */
0184         nla_total_size(0) +         /* VERDICT, nested */
0185             nla_total_size(sizeof(u32)) +   /* verdict code */
0186         nla_total_size(sizeof(u32)) +       /* id */
0187         nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
0188         nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
0189         nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
0190         nla_total_size(sizeof(u32)) +       /* iif */
0191         nla_total_size(sizeof(__be16)) +    /* iiftype */
0192         nla_total_size(sizeof(u32)) +       /* oif */
0193         nla_total_size(sizeof(__be16)) +    /* oiftype */
0194         nla_total_size(sizeof(u32)) +       /* mark */
0195         nla_total_size(sizeof(u32)) +       /* nfproto */
0196         nla_total_size(sizeof(u32));        /* policy */
0197 
0198     if (nft_trace_have_verdict_chain(info))
0199         size += nla_total_size(strlen(info->verdict->chain->name)); /* jump target */
0200 
0201     skb = nlmsg_new(size, GFP_ATOMIC);
0202     if (!skb)
0203         return;
0204 
0205     event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE);
0206     nlh = nfnl_msg_put(skb, 0, 0, event, 0, info->basechain->type->family,
0207                NFNETLINK_V0, 0);
0208     if (!nlh)
0209         goto nla_put_failure;
0210 
0211     if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt))))
0212         goto nla_put_failure;
0213 
0214     if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type)))
0215         goto nla_put_failure;
0216 
0217     if (nla_put_u32(skb, NFTA_TRACE_ID, info->skbid))
0218         goto nla_put_failure;
0219 
0220     if (nla_put_string(skb, NFTA_TRACE_CHAIN, info->chain->name))
0221         goto nla_put_failure;
0222 
0223     if (nla_put_string(skb, NFTA_TRACE_TABLE, info->chain->table->name))
0224         goto nla_put_failure;
0225 
0226     if (nf_trace_fill_rule_info(skb, info))
0227         goto nla_put_failure;
0228 
0229     switch (info->type) {
0230     case NFT_TRACETYPE_UNSPEC:
0231     case __NFT_TRACETYPE_MAX:
0232         break;
0233     case NFT_TRACETYPE_RETURN:
0234     case NFT_TRACETYPE_RULE:
0235         if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict))
0236             goto nla_put_failure;
0237 
0238         /* pkt->skb undefined iff NF_STOLEN, disable dump */
0239         if (info->verdict->code == NF_STOLEN)
0240             info->packet_dumped = true;
0241         else
0242             mark = pkt->skb->mark;
0243 
0244         break;
0245     case NFT_TRACETYPE_POLICY:
0246         mark = pkt->skb->mark;
0247 
0248         if (nla_put_be32(skb, NFTA_TRACE_POLICY,
0249                  htonl(info->basechain->policy)))
0250             goto nla_put_failure;
0251         break;
0252     }
0253 
0254     if (mark && nla_put_be32(skb, NFTA_TRACE_MARK, htonl(mark)))
0255         goto nla_put_failure;
0256 
0257     if (!info->packet_dumped) {
0258         if (nf_trace_fill_dev_info(skb, nft_in(pkt), nft_out(pkt)))
0259             goto nla_put_failure;
0260 
0261         if (nf_trace_fill_pkt_info(skb, pkt))
0262             goto nla_put_failure;
0263         info->packet_dumped = true;
0264     }
0265 
0266     nlmsg_end(skb, nlh);
0267     nfnetlink_send(skb, nft_net(pkt), 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC);
0268     return;
0269 
0270  nla_put_failure:
0271     WARN_ON_ONCE(1);
0272     kfree_skb(skb);
0273 }
0274 
0275 void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt,
0276             const struct nft_verdict *verdict,
0277             const struct nft_chain *chain)
0278 {
0279     static siphash_key_t trace_key __read_mostly;
0280     struct sk_buff *skb = pkt->skb;
0281 
0282     info->basechain = nft_base_chain(chain);
0283     info->trace = true;
0284     info->nf_trace = pkt->skb->nf_trace;
0285     info->packet_dumped = false;
0286     info->pkt = pkt;
0287     info->verdict = verdict;
0288 
0289     net_get_random_once(&trace_key, sizeof(trace_key));
0290 
0291     info->skbid = (u32)siphash_3u32(hash32_ptr(skb),
0292                     skb_get_hash(skb),
0293                     skb->skb_iif,
0294                     &trace_key);
0295 }