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