nft_chain_route.c (4065B)
1// SPDX-License-Identifier: GPL-2.0 2 3#include <linux/skbuff.h> 4#include <linux/netfilter.h> 5#include <linux/netfilter_ipv4.h> 6#include <linux/netfilter_ipv6.h> 7#include <linux/netfilter/nfnetlink.h> 8#include <linux/netfilter/nf_tables.h> 9#include <net/netfilter/nf_tables.h> 10#include <net/netfilter/nf_tables_ipv4.h> 11#include <net/netfilter/nf_tables_ipv6.h> 12#include <net/route.h> 13#include <net/ip.h> 14 15#ifdef CONFIG_NF_TABLES_IPV4 16static unsigned int nf_route_table_hook4(void *priv, 17 struct sk_buff *skb, 18 const struct nf_hook_state *state) 19{ 20 const struct iphdr *iph; 21 struct nft_pktinfo pkt; 22 __be32 saddr, daddr; 23 unsigned int ret; 24 u32 mark; 25 int err; 26 u8 tos; 27 28 nft_set_pktinfo(&pkt, skb, state); 29 nft_set_pktinfo_ipv4(&pkt); 30 31 mark = skb->mark; 32 iph = ip_hdr(skb); 33 saddr = iph->saddr; 34 daddr = iph->daddr; 35 tos = iph->tos; 36 37 ret = nft_do_chain(&pkt, priv); 38 if (ret == NF_ACCEPT) { 39 iph = ip_hdr(skb); 40 41 if (iph->saddr != saddr || 42 iph->daddr != daddr || 43 skb->mark != mark || 44 iph->tos != tos) { 45 err = ip_route_me_harder(state->net, state->sk, skb, RTN_UNSPEC); 46 if (err < 0) 47 ret = NF_DROP_ERR(err); 48 } 49 } 50 return ret; 51} 52 53static const struct nft_chain_type nft_chain_route_ipv4 = { 54 .name = "route", 55 .type = NFT_CHAIN_T_ROUTE, 56 .family = NFPROTO_IPV4, 57 .hook_mask = (1 << NF_INET_LOCAL_OUT), 58 .hooks = { 59 [NF_INET_LOCAL_OUT] = nf_route_table_hook4, 60 }, 61}; 62#endif 63 64#ifdef CONFIG_NF_TABLES_IPV6 65static unsigned int nf_route_table_hook6(void *priv, 66 struct sk_buff *skb, 67 const struct nf_hook_state *state) 68{ 69 struct in6_addr saddr, daddr; 70 struct nft_pktinfo pkt; 71 u32 mark, flowlabel; 72 unsigned int ret; 73 u8 hop_limit; 74 int err; 75 76 nft_set_pktinfo(&pkt, skb, state); 77 nft_set_pktinfo_ipv6(&pkt); 78 79 /* save source/dest address, mark, hoplimit, flowlabel, priority */ 80 memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr)); 81 memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr)); 82 mark = skb->mark; 83 hop_limit = ipv6_hdr(skb)->hop_limit; 84 85 /* flowlabel and prio (includes version, which shouldn't change either)*/ 86 flowlabel = *((u32 *)ipv6_hdr(skb)); 87 88 ret = nft_do_chain(&pkt, priv); 89 if (ret == NF_ACCEPT && 90 (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || 91 memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || 92 skb->mark != mark || 93 ipv6_hdr(skb)->hop_limit != hop_limit || 94 flowlabel != *((u32 *)ipv6_hdr(skb)))) { 95 err = nf_ip6_route_me_harder(state->net, state->sk, skb); 96 if (err < 0) 97 ret = NF_DROP_ERR(err); 98 } 99 100 return ret; 101} 102 103static const struct nft_chain_type nft_chain_route_ipv6 = { 104 .name = "route", 105 .type = NFT_CHAIN_T_ROUTE, 106 .family = NFPROTO_IPV6, 107 .hook_mask = (1 << NF_INET_LOCAL_OUT), 108 .hooks = { 109 [NF_INET_LOCAL_OUT] = nf_route_table_hook6, 110 }, 111}; 112#endif 113 114#ifdef CONFIG_NF_TABLES_INET 115static unsigned int nf_route_table_inet(void *priv, 116 struct sk_buff *skb, 117 const struct nf_hook_state *state) 118{ 119 struct nft_pktinfo pkt; 120 121 switch (state->pf) { 122 case NFPROTO_IPV4: 123 return nf_route_table_hook4(priv, skb, state); 124 case NFPROTO_IPV6: 125 return nf_route_table_hook6(priv, skb, state); 126 default: 127 nft_set_pktinfo(&pkt, skb, state); 128 break; 129 } 130 131 return nft_do_chain(&pkt, priv); 132} 133 134static const struct nft_chain_type nft_chain_route_inet = { 135 .name = "route", 136 .type = NFT_CHAIN_T_ROUTE, 137 .family = NFPROTO_INET, 138 .hook_mask = (1 << NF_INET_LOCAL_OUT), 139 .hooks = { 140 [NF_INET_LOCAL_OUT] = nf_route_table_inet, 141 }, 142}; 143#endif 144 145void __init nft_chain_route_init(void) 146{ 147#ifdef CONFIG_NF_TABLES_IPV6 148 nft_register_chain_type(&nft_chain_route_ipv6); 149#endif 150#ifdef CONFIG_NF_TABLES_IPV4 151 nft_register_chain_type(&nft_chain_route_ipv4); 152#endif 153#ifdef CONFIG_NF_TABLES_INET 154 nft_register_chain_type(&nft_chain_route_inet); 155#endif 156} 157 158void __exit nft_chain_route_fini(void) 159{ 160#ifdef CONFIG_NF_TABLES_IPV6 161 nft_unregister_chain_type(&nft_chain_route_ipv6); 162#endif 163#ifdef CONFIG_NF_TABLES_IPV4 164 nft_unregister_chain_type(&nft_chain_route_ipv4); 165#endif 166#ifdef CONFIG_NF_TABLES_INET 167 nft_unregister_chain_type(&nft_chain_route_inet); 168#endif 169}