0001
0002
0003 #include <linux/skbuff.h>
0004 #include <linux/netfilter.h>
0005 #include <linux/netfilter_ipv4.h>
0006 #include <linux/netfilter_ipv6.h>
0007 #include <linux/netfilter/nfnetlink.h>
0008 #include <linux/netfilter/nf_tables.h>
0009 #include <net/netfilter/nf_tables.h>
0010 #include <net/netfilter/nf_tables_ipv4.h>
0011 #include <net/netfilter/nf_tables_ipv6.h>
0012 #include <net/route.h>
0013 #include <net/ip.h>
0014
0015 #ifdef CONFIG_NF_TABLES_IPV4
0016 static unsigned int nf_route_table_hook4(void *priv,
0017 struct sk_buff *skb,
0018 const struct nf_hook_state *state)
0019 {
0020 const struct iphdr *iph;
0021 struct nft_pktinfo pkt;
0022 __be32 saddr, daddr;
0023 unsigned int ret;
0024 u32 mark;
0025 int err;
0026 u8 tos;
0027
0028 nft_set_pktinfo(&pkt, skb, state);
0029 nft_set_pktinfo_ipv4(&pkt);
0030
0031 mark = skb->mark;
0032 iph = ip_hdr(skb);
0033 saddr = iph->saddr;
0034 daddr = iph->daddr;
0035 tos = iph->tos;
0036
0037 ret = nft_do_chain(&pkt, priv);
0038 if (ret == NF_ACCEPT) {
0039 iph = ip_hdr(skb);
0040
0041 if (iph->saddr != saddr ||
0042 iph->daddr != daddr ||
0043 skb->mark != mark ||
0044 iph->tos != tos) {
0045 err = ip_route_me_harder(state->net, state->sk, skb, RTN_UNSPEC);
0046 if (err < 0)
0047 ret = NF_DROP_ERR(err);
0048 }
0049 }
0050 return ret;
0051 }
0052
0053 static const struct nft_chain_type nft_chain_route_ipv4 = {
0054 .name = "route",
0055 .type = NFT_CHAIN_T_ROUTE,
0056 .family = NFPROTO_IPV4,
0057 .hook_mask = (1 << NF_INET_LOCAL_OUT),
0058 .hooks = {
0059 [NF_INET_LOCAL_OUT] = nf_route_table_hook4,
0060 },
0061 };
0062 #endif
0063
0064 #ifdef CONFIG_NF_TABLES_IPV6
0065 static unsigned int nf_route_table_hook6(void *priv,
0066 struct sk_buff *skb,
0067 const struct nf_hook_state *state)
0068 {
0069 struct in6_addr saddr, daddr;
0070 struct nft_pktinfo pkt;
0071 u32 mark, flowlabel;
0072 unsigned int ret;
0073 u8 hop_limit;
0074 int err;
0075
0076 nft_set_pktinfo(&pkt, skb, state);
0077 nft_set_pktinfo_ipv6(&pkt);
0078
0079
0080 memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr));
0081 memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr));
0082 mark = skb->mark;
0083 hop_limit = ipv6_hdr(skb)->hop_limit;
0084
0085
0086 flowlabel = *((u32 *)ipv6_hdr(skb));
0087
0088 ret = nft_do_chain(&pkt, priv);
0089 if (ret == NF_ACCEPT &&
0090 (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
0091 memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
0092 skb->mark != mark ||
0093 ipv6_hdr(skb)->hop_limit != hop_limit ||
0094 flowlabel != *((u32 *)ipv6_hdr(skb)))) {
0095 err = nf_ip6_route_me_harder(state->net, state->sk, skb);
0096 if (err < 0)
0097 ret = NF_DROP_ERR(err);
0098 }
0099
0100 return ret;
0101 }
0102
0103 static const struct nft_chain_type nft_chain_route_ipv6 = {
0104 .name = "route",
0105 .type = NFT_CHAIN_T_ROUTE,
0106 .family = NFPROTO_IPV6,
0107 .hook_mask = (1 << NF_INET_LOCAL_OUT),
0108 .hooks = {
0109 [NF_INET_LOCAL_OUT] = nf_route_table_hook6,
0110 },
0111 };
0112 #endif
0113
0114 #ifdef CONFIG_NF_TABLES_INET
0115 static unsigned int nf_route_table_inet(void *priv,
0116 struct sk_buff *skb,
0117 const struct nf_hook_state *state)
0118 {
0119 struct nft_pktinfo pkt;
0120
0121 switch (state->pf) {
0122 case NFPROTO_IPV4:
0123 return nf_route_table_hook4(priv, skb, state);
0124 case NFPROTO_IPV6:
0125 return nf_route_table_hook6(priv, skb, state);
0126 default:
0127 nft_set_pktinfo(&pkt, skb, state);
0128 break;
0129 }
0130
0131 return nft_do_chain(&pkt, priv);
0132 }
0133
0134 static const struct nft_chain_type nft_chain_route_inet = {
0135 .name = "route",
0136 .type = NFT_CHAIN_T_ROUTE,
0137 .family = NFPROTO_INET,
0138 .hook_mask = (1 << NF_INET_LOCAL_OUT),
0139 .hooks = {
0140 [NF_INET_LOCAL_OUT] = nf_route_table_inet,
0141 },
0142 };
0143 #endif
0144
0145 void __init nft_chain_route_init(void)
0146 {
0147 #ifdef CONFIG_NF_TABLES_IPV6
0148 nft_register_chain_type(&nft_chain_route_ipv6);
0149 #endif
0150 #ifdef CONFIG_NF_TABLES_IPV4
0151 nft_register_chain_type(&nft_chain_route_ipv4);
0152 #endif
0153 #ifdef CONFIG_NF_TABLES_INET
0154 nft_register_chain_type(&nft_chain_route_inet);
0155 #endif
0156 }
0157
0158 void __exit nft_chain_route_fini(void)
0159 {
0160 #ifdef CONFIG_NF_TABLES_IPV6
0161 nft_unregister_chain_type(&nft_chain_route_ipv6);
0162 #endif
0163 #ifdef CONFIG_NF_TABLES_IPV4
0164 nft_unregister_chain_type(&nft_chain_route_ipv4);
0165 #endif
0166 #ifdef CONFIG_NF_TABLES_INET
0167 nft_unregister_chain_type(&nft_chain_route_inet);
0168 #endif
0169 }