1 // SPDX-License-Identifier: GPL-2.0
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>
15 #ifdef CONFIG_NF_TABLES_IPV4
16 static unsigned int nf_route_table_hook4(void *priv
,
18 const struct nf_hook_state
*state
)
20 const struct iphdr
*iph
;
21 struct nft_pktinfo pkt
;
28 nft_set_pktinfo(&pkt
, skb
, state
);
29 nft_set_pktinfo_ipv4(&pkt
);
37 ret
= nft_do_chain(&pkt
, priv
);
38 if (ret
== NF_ACCEPT
) {
41 if (iph
->saddr
!= saddr
||
42 iph
->daddr
!= daddr
||
45 err
= ip_route_me_harder(state
->net
, state
->sk
, skb
, RTN_UNSPEC
);
47 ret
= NF_DROP_ERR(err
);
53 static const struct nft_chain_type nft_chain_route_ipv4
= {
55 .type
= NFT_CHAIN_T_ROUTE
,
56 .family
= NFPROTO_IPV4
,
57 .hook_mask
= (1 << NF_INET_LOCAL_OUT
),
59 [NF_INET_LOCAL_OUT
] = nf_route_table_hook4
,
64 #ifdef CONFIG_NF_TABLES_IPV6
65 static unsigned int nf_route_table_hook6(void *priv
,
67 const struct nf_hook_state
*state
)
69 struct in6_addr saddr
, daddr
;
70 struct nft_pktinfo pkt
;
76 nft_set_pktinfo(&pkt
, skb
, state
);
77 nft_set_pktinfo_ipv6(&pkt
);
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
));
83 hop_limit
= ipv6_hdr(skb
)->hop_limit
;
85 /* flowlabel and prio (includes version, which shouldn't change either)*/
86 flowlabel
= *((u32
*)ipv6_hdr(skb
));
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
)) ||
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
);
97 ret
= NF_DROP_ERR(err
);
103 static const struct nft_chain_type nft_chain_route_ipv6
= {
105 .type
= NFT_CHAIN_T_ROUTE
,
106 .family
= NFPROTO_IPV6
,
107 .hook_mask
= (1 << NF_INET_LOCAL_OUT
),
109 [NF_INET_LOCAL_OUT
] = nf_route_table_hook6
,
114 #ifdef CONFIG_NF_TABLES_INET
115 static unsigned int nf_route_table_inet(void *priv
,
117 const struct nf_hook_state
*state
)
119 struct nft_pktinfo pkt
;
123 return nf_route_table_hook4(priv
, skb
, state
);
125 return nf_route_table_hook6(priv
, skb
, state
);
127 nft_set_pktinfo(&pkt
, skb
, state
);
131 return nft_do_chain(&pkt
, priv
);
134 static const struct nft_chain_type nft_chain_route_inet
= {
136 .type
= NFT_CHAIN_T_ROUTE
,
137 .family
= NFPROTO_INET
,
138 .hook_mask
= (1 << NF_INET_LOCAL_OUT
),
140 [NF_INET_LOCAL_OUT
] = nf_route_table_inet
,
145 void __init
nft_chain_route_init(void)
147 #ifdef CONFIG_NF_TABLES_IPV6
148 nft_register_chain_type(&nft_chain_route_ipv6
);
150 #ifdef CONFIG_NF_TABLES_IPV4
151 nft_register_chain_type(&nft_chain_route_ipv4
);
153 #ifdef CONFIG_NF_TABLES_INET
154 nft_register_chain_type(&nft_chain_route_inet
);
158 void __exit
nft_chain_route_fini(void)
160 #ifdef CONFIG_NF_TABLES_IPV6
161 nft_unregister_chain_type(&nft_chain_route_ipv6
);
163 #ifdef CONFIG_NF_TABLES_IPV4
164 nft_unregister_chain_type(&nft_chain_route_ipv4
);
166 #ifdef CONFIG_NF_TABLES_INET
167 nft_unregister_chain_type(&nft_chain_route_inet
);