Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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> |
Florian Westphal | e639f7a | 2015-11-28 21:53:05 +0100 | [diff] [blame] | 11 | #include <linux/static_key.h> |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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 | |
Florian Westphal | e639f7a | 2015-11-28 21:53:05 +0100 | [diff] [blame] | 28 | DEFINE_STATIC_KEY_FALSE(nft_trace_enabled); |
| 29 | EXPORT_SYMBOL_GPL(nft_trace_enabled); |
| 30 | |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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; |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 116 | int off = skb_network_offset(skb); |
Liping Zhang | a20877b | 2016-09-17 14:31:20 +0800 | [diff] [blame] | 117 | unsigned int len, nh_end; |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 118 | |
Liping Zhang | a20877b | 2016-09-17 14:31:20 +0800 | [diff] [blame] | 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); |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 122 | if (trace_fill_header(nlskb, NFTA_TRACE_NETWORK_HEADER, skb, off, len)) |
| 123 | return -1; |
| 124 | |
Liping Zhang | a20877b | 2016-09-17 14:31:20 +0800 | [diff] [blame] | 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 | } |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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, |
Nicolas Dichtel | b46f6de | 2016-04-22 17:31:18 +0200 | [diff] [blame] | 161 | cpu_to_be64(info->rule->handle), |
| 162 | NFTA_TRACE_PAD); |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | void 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) + |
Nicolas Dichtel | b46f6de | 2016-04-22 17:31:18 +0200 | [diff] [blame] | 180 | nla_total_size_64bit(sizeof(__be64)) + /* rule handle */ |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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, |
Liping Zhang | 5210d39 | 2016-09-02 20:49:12 +0800 | [diff] [blame] | 242 | htonl(info->basechain->policy))) |
Florian Westphal | 33d5a7b | 2015-11-28 21:53:04 +0100 | [diff] [blame] | 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 | |
| 269 | void 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 | } |