This extends log statement to support the behaviour achieved with AUDIT target in iptables. Audit logging is enabled via a pseudo log level 8. In this case any other settings like log prefix are ignored since audit log format is fixed.
Unified · Split
| @@ -1080,6 +1080,11 @@ enum nft_log_attributes { · 1079 unmodified lines | ||
| 1080 | 1080 | }; |
| 1081 | 1081 | #define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1) |
| 1082 | 1082 | |
| 1083 | +/** | |
| 1084 | + * LOGLEVEL_AUDIT - a pseudo log level enabling audit logging | |
| 1085 | + */ | |
| 1086 | +#define LOGLEVEL_AUDIT 8 | |
| 1087 | + | |
| 1083 | 1088 | /** |
| 1084 | 1089 | * enum nft_queue_attributes - nf_tables queue expression netlink attributes |
| 1085 | 1090 | * |
| @@ -9,12 +9,15 @@ · 8 unmodified lines | ||
| 9 | 9 | * Development of this code funded by Astaro AG (http://www.astaro.com/) |
| 10 | 10 | */ |
| 11 | 11 | |
| 12 | +#include <linux/audit.h> | |
| 12 | 13 | #include <linux/kernel.h> |
| 13 | 14 | #include <linux/init.h> |
| 14 | 15 | #include <linux/module.h> |
| 15 | 16 | #include <linux/netlink.h> |
| 16 | 17 | #include <linux/netfilter.h> |
| 17 | 18 | #include <linux/netfilter/nf_tables.h> |
| 19 | +#include <net/ipv6.h> | |
| 20 | +#include <net/ip.h> | |
| 18 | 21 | #include <net/netfilter/nf_tables.h> |
| 19 | 22 | #include <net/netfilter/nf_log.h> |
| 20 | 23 | #include <linux/netdevice.h> |
| @@ -26,12 +29,93 @@ struct nft_log { · 5 unmodified lines | ||
| 26 | 29 | char *prefix; |
| 27 | 30 | }; |
| 28 | 31 | |
| 32 | +static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb) | |
| 33 | +{ | |
| 34 | + struct iphdr _iph; | |
| 35 | + const struct iphdr *ih; | |
| 36 | + | |
| 37 | + ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph); | |
| 38 | + if (!ih) | |
| 39 | + return false; | |
| 40 | + | |
| 41 | + audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu", | |
| 42 | + &ih->saddr, &ih->daddr, ih->protocol); | |
| 43 | + | |
| 44 | + return true; | |
| 45 | +} | |
| 46 | + | |
| 47 | +static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) | |
| 48 | +{ | |
| 49 | + struct ipv6hdr _ip6h; | |
| 50 | + const struct ipv6hdr *ih; | |
| 51 | + u8 nexthdr; | |
| 52 | + __be16 frag_off; | |
| 53 | + | |
| 54 | + ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h); | |
| 55 | + if (!ih) | |
| 56 | + return false; | |
| 57 | + | |
| 58 | + nexthdr = ih->nexthdr; | |
| 59 | + ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off); | |
| 60 | + | |
| 61 | + audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", | |
| 62 | + &ih->saddr, &ih->daddr, nexthdr); | |
| 63 | + | |
| 64 | + return true; | |
| 65 | +} | |
| 66 | + | |
| 67 | +static void nft_log_eval_audit(const struct nft_pktinfo *pkt) | |
| 68 | +{ | |
| 69 | + struct sk_buff *skb = pkt->skb; | |
| 70 | + struct audit_buffer *ab; | |
| 71 | + int fam = -1; | |
| 72 | + | |
| 73 | + if (!audit_enabled) | |
| 74 | + return; | |
| 75 | + | |
| 76 | + ab = audit_log_start(NULL, GFP_ATOMIC, AUDIT_NETFILTER_PKT); | |
| 77 | + if (!ab) | |
| 78 | + return; | |
| 79 | + | |
| 80 | + audit_log_format(ab, "mark=%#x", skb->mark); | |
| 81 | + | |
| 82 | + switch (nft_pf(pkt)) { | |
| 83 | + case NFPROTO_BRIDGE: | |
| 84 | + switch (eth_hdr(skb)->h_proto) { | |
| 85 | + case htons(ETH_P_IP): | |
| 86 | + fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; | |
| 87 | + break; | |
| 88 | + case htons(ETH_P_IPV6): | |
| 89 | + fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; | |
| 90 | + break; | |
| 91 | + } | |
| 92 | + break; | |
| 93 | + case NFPROTO_IPV4: | |
| 94 | + fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; | |
| 95 | + break; | |
| 96 | + case NFPROTO_IPV6: | |
| 97 | + fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; | |
| 98 | + break; | |
| 99 | + } | |
| 100 | + | |
| 101 | + if (fam == -1) | |
| 102 | + audit_log_format(ab, " saddr=? daddr=? proto=-1"); | |
| 103 | + | |
| 104 | + audit_log_end(ab); | |
| 105 | +} | |
| 106 | + | |
| 29 | 107 | static void nft_log_eval(const struct nft_expr *expr, |
| 30 | 108 | struct nft_regs *regs, |
| 31 | 109 | const struct nft_pktinfo *pkt) |
| 32 | 110 | { |
| 33 | 111 | const struct nft_log *priv = nft_expr_priv(expr); |
| 34 | 112 | |
| 113 | + if (priv->loginfo.type == NF_LOG_TYPE_LOG && | |
| 114 | + priv->loginfo.u.log.level == LOGLEVEL_AUDIT) { | |
| 115 | + nft_log_eval_audit(pkt); | |
| 116 | + return; | |
| 117 | + } | |
| 118 | + | |
| 35 | 119 | nf_log_packet(nft_net(pkt), nft_pf(pkt), nft_hook(pkt), pkt->skb, |
| 36 | 120 | nft_in(pkt), nft_out(pkt), &priv->loginfo, "%s", |
| 37 | 121 | priv->prefix); |
| @@ -84,7 +168,7 @@ static int nft_log_init(const struct nft_ctx *ctx, · 46 unmodified lines | ||
| 84 | 168 | } else { |
| 85 | 169 | li->u.log.level = LOGLEVEL_WARNING; |
| 86 | 170 | } |
| 87 | − if (li->u.log.level > LOGLEVEL_DEBUG) { | |
| 171 | + if (li->u.log.level > LOGLEVEL_AUDIT) { | |
| 88 | 172 | err = -EINVAL; |
| 89 | 173 | goto err1; |
| 90 | 174 | } |
| @@ -112,6 +196,9 @@ static int nft_log_init(const struct nft_ctx *ctx, · 21 unmodified lines | ||
| 112 | 196 | break; |
| 113 | 197 | } |
| 114 | 198 | |
| 199 | + if (li->u.log.level == LOGLEVEL_AUDIT) | |
| 200 | + return 0; | |
| 201 | + | |
| 115 | 202 | err = nf_logger_find_get(ctx->family, li->type); |
| 116 | 203 | if (err < 0) |
| 117 | 204 | goto err1; |
| @@ -133,6 +220,9 @@ static void nft_log_destroy(const struct nft_ctx *ctx, · 15 unmodified lines | ||
| 133 | 220 | if (priv->prefix != nft_log_null_prefix) |
| 134 | 221 | kfree(priv->prefix); |
| 135 | 222 | |
| 223 | + if (li->u.log.level == LOGLEVEL_AUDIT) | |
| 224 | + return; | |
| 225 | + | |
| 136 | 226 | nf_logger_put(ctx->family, li->type); |
| 137 | 227 | } |
| 138 | 228 | |