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 28 DEFINE_STATIC_KEY_FALSE(nft_trace_enabled); 29 EXPORT_SYMBOL_GPL(nft_trace_enabled); 30 31 static 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 47 static 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 63 static 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 85 static 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 112 static 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 145 static 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 165 static bool nft_trace_have_verdict_chain(struct nft_traceinfo *info) 166 { 167 switch (info->type) { 168 case NFT_TRACETYPE_RETURN: 169 case NFT_TRACETYPE_RULE: 170 break; 171 default: 172 return false; 173 } 174 175 switch (info->verdict->code) { 176 case NFT_JUMP: 177 case NFT_GOTO: 178 break; 179 default: 180 return false; 181 } 182 183 return true; 184 } 185 186 void nft_trace_notify(struct nft_traceinfo *info) 187 { 188 const struct nft_pktinfo *pkt = info->pkt; 189 struct nfgenmsg *nfmsg; 190 struct nlmsghdr *nlh; 191 struct sk_buff *skb; 192 unsigned int size; 193 u16 event; 194 195 if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE)) 196 return; 197 198 size = nlmsg_total_size(sizeof(struct nfgenmsg)) + 199 nla_total_size(strlen(info->chain->table->name)) + 200 nla_total_size(strlen(info->chain->name)) + 201 nla_total_size_64bit(sizeof(__be64)) + /* rule handle */ 202 nla_total_size(sizeof(__be32)) + /* trace type */ 203 nla_total_size(0) + /* VERDICT, nested */ 204 nla_total_size(sizeof(u32)) + /* verdict code */ 205 nla_total_size(sizeof(u32)) + /* id */ 206 nla_total_size(NFT_TRACETYPE_LL_HSIZE) + 207 nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) + 208 nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) + 209 nla_total_size(sizeof(u32)) + /* iif */ 210 nla_total_size(sizeof(__be16)) + /* iiftype */ 211 nla_total_size(sizeof(u32)) + /* oif */ 212 nla_total_size(sizeof(__be16)) + /* oiftype */ 213 nla_total_size(sizeof(u32)) + /* mark */ 214 nla_total_size(sizeof(u32)) + /* nfproto */ 215 nla_total_size(sizeof(u32)); /* policy */ 216 217 if (nft_trace_have_verdict_chain(info)) 218 size += nla_total_size(strlen(info->verdict->chain->name)); /* jump target */ 219 220 skb = nlmsg_new(size, GFP_ATOMIC); 221 if (!skb) 222 return; 223 224 event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE); 225 nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0); 226 if (!nlh) 227 goto nla_put_failure; 228 229 nfmsg = nlmsg_data(nlh); 230 nfmsg->nfgen_family = info->basechain->type->family; 231 nfmsg->version = NFNETLINK_V0; 232 nfmsg->res_id = 0; 233 234 if (nla_put_be32(skb, NFTA_TRACE_NFPROTO, htonl(nft_pf(pkt)))) 235 goto nla_put_failure; 236 237 if (nla_put_be32(skb, NFTA_TRACE_TYPE, htonl(info->type))) 238 goto nla_put_failure; 239 240 if (trace_fill_id(skb, pkt->skb)) 241 goto nla_put_failure; 242 243 if (nla_put_string(skb, NFTA_TRACE_CHAIN, info->chain->name)) 244 goto nla_put_failure; 245 246 if (nla_put_string(skb, NFTA_TRACE_TABLE, info->chain->table->name)) 247 goto nla_put_failure; 248 249 if (nf_trace_fill_rule_info(skb, info)) 250 goto nla_put_failure; 251 252 switch (info->type) { 253 case NFT_TRACETYPE_UNSPEC: 254 case __NFT_TRACETYPE_MAX: 255 break; 256 case NFT_TRACETYPE_RETURN: 257 case NFT_TRACETYPE_RULE: 258 if (nft_verdict_dump(skb, NFTA_TRACE_VERDICT, info->verdict)) 259 goto nla_put_failure; 260 break; 261 case NFT_TRACETYPE_POLICY: 262 if (nla_put_be32(skb, NFTA_TRACE_POLICY, 263 htonl(info->basechain->policy))) 264 goto nla_put_failure; 265 break; 266 } 267 268 if (pkt->skb->mark && 269 nla_put_be32(skb, NFTA_TRACE_MARK, htonl(pkt->skb->mark))) 270 goto nla_put_failure; 271 272 if (!info->packet_dumped) { 273 if (nf_trace_fill_dev_info(skb, nft_in(pkt), nft_out(pkt))) 274 goto nla_put_failure; 275 276 if (nf_trace_fill_pkt_info(skb, pkt)) 277 goto nla_put_failure; 278 info->packet_dumped = true; 279 } 280 281 nlmsg_end(skb, nlh); 282 nfnetlink_send(skb, nft_net(pkt), 0, NFNLGRP_NFTRACE, 0, GFP_ATOMIC); 283 return; 284 285 nla_put_failure: 286 WARN_ON_ONCE(1); 287 kfree_skb(skb); 288 } 289 290 void nft_trace_init(struct nft_traceinfo *info, const struct nft_pktinfo *pkt, 291 const struct nft_verdict *verdict, 292 const struct nft_chain *chain) 293 { 294 info->basechain = nft_base_chain(chain); 295 info->trace = true; 296 info->packet_dumped = false; 297 info->pkt = pkt; 298 info->verdict = verdict; 299 } 300